如何准确计算Java对象的大小

原创文章,转载请注明:博客园aprogramer

原文链接:如何准确计算Java对象的大小

     有时,我们需要知道Java对象到底占用多少内存,有人通过连续调用两次System.gc()比较两次gc前后内存的使用量在计算java对象的大小,也有人根据Java虚拟机规范中的Java对象内存排列估算对象的大小,这两种方法或多或少都有问题,因为System.gc()并不一定促发GC,同一个类型的对象在32位与64位JVM中使用的内存会不一样,在64位虚拟机中是否开启指针压缩也会影响Java对象在内存中的大小。
 
     那么有没有一种既准确又方便的方法计算对象的大小呢?答案是肯定的。在Java 5中引入了Instrumentation类,这个类提供了计算对象内存占用量的方法;Hotspot支持instrumentation框架,其他的虚拟机也提供了类似的框架
 
使用Instrumentation类计算Java对象大小的过程如下:
  • 创建一个有premain方法的agent 类,
  • JVM在调用agent类的premain方法时会传入一个Instrumentation 对象,调用Instrumentation的getObjectSize方法
  • 把agent类打成一个jar包
  • 启动我们的应用程序,使用JVM参数指定agent jar的路径
下面以计算Object对象的大小为例,详细介绍使用Instrumentation计算对象大小的过程

1. 创建Instrumentation agent类

Instrumentation agent类有一个方法premain,声明如下:
 public static void premain(String args, Instrumentation inst) {
...
}

JVM会在应用程序运行之前调用这个方法(也就是在执行应用程序的main方法之前),JVM会在调用该方法时传入一个实现Instrumentation接口的实例,此时我们就可以调用getObjectSize()方法来计算对象的大小。例如我们要计算Object实例和自定义类型MyObject实例的大小,agent代码如下:

 package my;
import java.lang.instrument.Instrumentation; public class MyAgent {
public static void premain(String args, Instrumentation inst) {
Object obj = new Object();
System.out.println("Bytes used by Object:"+ inst.getObjectSize(obj));
System.out.println("Bytes used by MyObject:"+ inst.getObjectSize(new MyObject()));
}
public static void main(String[] args) {
System.out.println("main is over");
}
}

MyObject代码如下:

 package my;

 public class MyObject{
Object object = new Object();
}
需要注意的是agent类不需要实现任何接口,只需要定义premain方法就行,JVM会自动调用该方法。

2. 把agent类打包成jar包

在打包之前需要创建manifest 文件,创建manifest.txt文件,包括以下内容:
Premain-Class: my.MyAgent
然后执行一下命令创建jar包
jar -cmf manifest.txt agent.jar my/*

3.使用agent运行应用程序

运行应用程序,并使用javaagent命令行参数指定instrumentation agent的jar文件,加入classpath为当前目录并且main方法在com.mypackage.Main中,命令如下:
java -javaagent:agent.jar -cp . my.MyAgent

在32位机器上运行结果如下:

hadoop@32bithost:~/workspace/my/bin$ java -javaagent:agent.jar my.MyAgent
Bytes used by Object:8
Bytes used by MyObject:16
main is over

在64位机器上(不开启指针压缩)运行结果如下:

[genie.yjd@64bithost ~]$ java -XX:-UseCompressedOops -javaagent:agent.jar -cp .  my.MyAgent
Bytes used by Object:16
Bytes used by MyObject:24
main is over

在64位机器上(开启指针压缩)运行结果如下:

[genie.yjd@64bithost~]$ java -XX:+UseCompressedOops -javaagent:agent.jar -cp . my.MyAgent 
Bytes used by Object:16
Bytes used by MyObject:16
main is over

运行结果显示对于Object对象在32bit机器上占8个字节,在64bit机器上占16个字节,而对于用于一个Object类型成员的MyObject在32bit机器上占用16个字节,而在64bit机器上不开启指针压缩是占用24个字节,开启指针压缩后占用16个字节。

在应用程序中访问Instrumentation对象

在上面的例子中,我们在premain方法中计算对象的大小。可是如果我们想要在应用程序执行期间计算某个对象的大小该怎么办呢?
我们可以这样做,在premain方法中把Instrumentation对象保存在一个static引用中,然后提供一个static方法访问这个实例,代码如下:
 public class MyAgent {
private static volatile Instrumentation globalInstr;
public static void premain(String args, Instrumentation inst) {
globalInstr = inst;
}
public static long getObjectSize(Object obj) {
if (globalInstr == null)
throw new IllegalStateException("Agent not initted");
return globalInstr.getObjectSize(obj);
}
}

这样我们就可以在应用程序中调用 MyAgent.getObjectSize()来计算运行时任意实例的大小了。

深度计算对象内存使用

     注意getObjectSize方法不包括对象应用的其他对象的大小。加入对象A引用对象B,使用getObjectSize()方法计算对象A的大小时,只包括对象B引用的大小(4byte),而不是对象B的真是大小。如何深度计算对象的大小,我会在下一篇blog中详细说明。
 
 
 

如何准确计算Java对象的大小的更多相关文章

  1. 计算Java对象内存大小

    摘要 本文以如何计算Java对象占用内存大小为切入点,在讨论计算Java对象占用堆内存大小的方法的基础上,详细讨论了Java对象头格式并结合JDK源码对对象头中的协议字段做了介绍,涉及内存模型.锁原理 ...

  2. Ehcache计算Java对象内存大小

    在EHCache中,可以设置maxBytesLocalHeap.maxBytesLocalOffHeap.maxBytesLocalDisk值,以控制Cache占用的内存.磁盘的大小(注:这里Off ...

  3. JVM —— Java 对象占用空间大小计算

    零. 为什么要知道 Java 对象占用空间大小 缓存的实现: 在设计 JVM 内缓存时(不是借助 Memcached. Redis 等), 须要知道缓存的对象是否会超过 JVM 最大堆限制, 假设会超 ...

  4. 准确计算Java中对象的大小

    由于在项目中需要大致计算一下对象的内存占用率(Hadoop中的Reduce端内存占用居高不下却又无法解释),因此深入学习了一下如何准确计算对象的大小. 使用system.gc()和java.lang. ...

  5. 两种计算Java对象大小的方法

    之前想研究一下unsafe类,碰巧在网上看到了这篇文章,觉得写得很好,就转载过来.原文出处是: http://blog.csdn.net/iter_zc/article/details/4182271 ...

  6. 如何精确地测量java对象的大小-底层instrument API

    转载: 如何精确地测量java对象的大小-底层instrument API 关于java对象的大小测量,网上有很多例子,大多数是申请一个对象后开始做GC,后对比前后的大小,不过这样,虽然说这样测量对象 ...

  7. Java对象的大小及应用类型

    基础类型数据的大小是固定的,对于非基本类型的java对象,其大小就值得商榷了.      在java中一个空Object对象的大小是8byte,这个大小只是保存堆中没有任何属性的对象的大小,看下面的语 ...

  8. java对象内存大小评估

    Java对象的内存布局:对象头(Header).实例数据(Instance Data)和对齐填充(Padding).无论是32位还是64位的HotSpot,使用的都是8字节对齐.也就是说每个java对 ...

  9. 如何查看java对象的大小

    有时需要查看java对象占用了多少内存(对象大小),lucene为我们提供了一个很好的工具类,操作简单,如下: int[] s = new int[1024]; System.out.println( ...

随机推荐

  1. BZOJ 3196 Tyvj 1730 二逼平衡树:线段树套splay

    传送门 题意 给你一个长度为 $ n $ 有序数列 $ a $ ,进行 $ m $ 次操作,操作有如下几种: 查询 $ k $ 在区间 $ [l,r] $ 内的排名 查询区间 $ [l,r] $ 内排 ...

  2. MongoDB GridFS——本质上是将一个文件分割为大小为256KB的chunks 每个chunk里会放md5标识 取文件的时候会将这些chunks合并为一个整体返回

    MongoDB GridFS GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片.音频.视频等). GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中 ...

  3. 【spark】示例:求Top值

    我们有这样的两个文件 第一个数字为行号,后边为三列数据.我们来求第二列数据的Top(N) (1)我们先读取数据,创建Rdd (2)过滤数据,取第二列数据. 我们用filter()来过滤数据 line. ...

  4. Android开发调试中遇到的Waiting for HOME解决方案

    显示问题如图: 基本解决思路:在AVD里面单独启动运行一次模拟器,然后尝试运行程序,基本会解决这个问题.

  5. C#中的异常捕获机制(try catch finally)

    (转自:http://blog.csdn.net/zevin/article/details/6901489) 一.C#的异常处理所用到关键字try 用于检查发生的异常,并帮助发送任何可能的异常.ca ...

  6. 一些初学shell自己写的一些练习题脚本

    1斐波拉契数列前10个  #!/binbash#declare A=0declare B=1for ((i=1;i<6;i++)); do    let A+=B    printf " ...

  7. Developing on Windows Phone 8 Devices

    Developing on Windows Phone 8 Deviceshttp://docs.madewithmarmalade.com/native/platformguides/wp8guid ...

  8. PS常用美化处理方法大全

    学习PS的同学都知道,我们日常生活中使用PS就是进行一些简单的图像美白,图像颜色的优化,其他的基本不用,在长时间的PS使用过程中本人总结了一些处理皮肤的方法,都是一些非常简单的方法,希望能够帮助那些刚 ...

  9. 2017-2018-2 20179215《网络攻防实践》seed缓冲区溢出实验

    seed缓冲区溢出实验 有漏洞的程序: /* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our tas ...

  10. uoj #190. 【集训队互测2016】消失的源代码 提交答案题

    Test 1: 发现是一个字母表的映射 把 \('a' \to 'z'\) 打进去找出映射就好了QAQ . Test 2: 求助 \(dalao\) 得知的点.. 答案是 : \(2016x^2 + ...