Java对象内存大小检测方法对比:结果差异及选型疑问
Java对象内存大小检测:两种方法的差异及正确选择
嘿,我来帮你理清这两种方法到底在测什么,以及为什么结果差这么大——其实它们测的根本不是一回事儿!
先拆解两种方法的本质
1. Runtime内存差值法(Jon Skeet的方法)
这个方法是通过计算创建对象前后的堆内存使用差值来估算,但它有几个关键的局限性:
- GC的不确定性:哪怕你调用了
System.gc(),JVM也只是“建议”执行垃圾回收,不能保证立刻清干净。所以前后的内存差值可能混杂着其他未回收的对象,或者包含了JVM为了分配内存提前申请的整块内存(不是刚好对象需要的大小)。 - 测的是JVM实际分配的堆内存,不是对象本身的精确大小:JVM里每个对象都有对象头(Mark Word、类指针)、内存对齐的额外开销,数组还有单独的长度字段。比如你测的
LotsOfBooleans对象,实际在堆里占的内存远不止1字节的boolean字段,还要加上对象头和对齐填充,而Runtime方法算的是JVM给这些对象+数组分配的整块内存的变化,不是单个对象的精确内存占用。 - Jon Skeet的那个回答其实是用来大致监控堆内存的变化趋势,不是精确测量单个对象的内存占用哦。
2. ByteArrayOutputStream序列化法
这个方法测的是对象序列化后的字节大小,和堆内存里的实际占用完全是两码事:
- 序列化只会保存对象的字段数据和序列化所需的元信息(比如类的标识),会忽略JVM对象头、内存对齐、类元数据引用这些堆内存里的额外开销。
- 对于数组来说,序列化只记录数组长度和每个元素的字段数据,但堆里的数组本身有对象头,每个对象元素也有自己的对象头,这些都不会被序列化进去。
所以这个值只能用来判断对象在网络传输、存储时的大小,和它在JVM里占的内存没关系。
要精确测对象内存大小?用这些方法
如果你想知道对象在JVM堆里的实际内存占用,推荐这两种靠谱的方式:
方法一:用JOL(Java Object Layout)工具
这是OpenJDK官方推出的工具,能直接展示对象的内存布局和精确大小,用起来超方便,不需要写agent。比如给你的测试类加一段代码:
import org.openjdk.jol.info.ClassLayout; public class Test { // 你的原有代码... public static void main(String[] args) throws Exception { // 原有代码... // 用JOL查看对象内存布局 System.out.println("LotsOfBooleans对象内存布局:"); System.out.println(ClassLayout.parseClass(LotsOfBooleans.class).toPrintable()); System.out.println("\nLotsOfInts对象内存布局:"); System.out.println(ClassLayout.parseClass(LotsOfInts.class).toPrintable()); } }
它会输出详细的内存结构,包括对象头的大小、字段偏移、对齐字节,还有总大小,一目了然。
方法二:用JDK的Instrumentation API
这是最底层最准确的方法,能获取对象的浅内存大小(不包括引用对象的内存)。你需要写一个简单的Java Agent:
import java.lang.instrument.Instrumentation; public class ObjectSizeAgent { private static Instrumentation inst; public static void premain(String agentArgs, Instrumentation instrumentation) { inst = instrumentation; } public static long getObjectSize(Object obj) { return inst.getObjectSize(obj); } }
然后打包成jar,在MANIFEST.MF里指定Premain-Class: ObjectSizeAgent,运行时用-javaagent:你的agent.jar参数加载,之后就能调用ObjectSizeAgent.getObjectSize()获取精确的对象大小了。
方法三:手动计算(适合理解内存模型)
如果只是想学习,也可以根据JVM的内存规则手动算:
- 64位JVM默认对象头是16字节(开启指针压缩的话是12字节);
- 实例字段按类型占对应字节(boolean1、int4等),还要对齐到8字节的倍数;
- 数组对象额外加4字节存长度,再加上元素内存和对齐。
比如开启指针压缩时,LotsOfBooleans对象是12字节头+1字节boolean,对齐到16字节,100万个的话就是16*1000000=16000000字节,和你Runtime方法得到的15914240接近,因为还要减去数组本身的内存开销。
最后总结一下
- Runtime差值法:适合看堆内存整体变化趋势,别用来精确测对象大小;
- 序列化法:测的是对象序列化后的字节数,和堆内存无关;
- 要精确测JVM堆里的对象大小,优先用JOL工具,简单直观;追求底层准确就用Instrumentation API。
内容的提问来源于stack exchange,提问作者Elye
相关产品推荐
相关产品推荐

