不能。Java无sizeof运算符,对象大小需用Instrumentation.getObjectSize()(需Java Agent)或JOL工具获取,二者因测量粒度和时机不同结果常不一致,且受JVM版本、平台、压缩指针等影响。

Java对象内存大小到底能不能靠 sizeof 算?
不能。Java没有 sizeof 运算符,JVM也不暴露原始内存布局给用户直接读取。你看到的“对象大小”其实是JVM堆中该实例实际占用的字节数,包含对象头、字段、对齐填充——而且不同JVM(HotSpot vs OpenJ9)、不同版本、不同平台(x86_64 vs aarch64)、是否开启压缩指针(-XX:+UseCompressedOops)都会影响结果。
所以别试图用 Object.hashCode() 或地址差值去估算,那只是幻觉。
用 Instrumentation.getObjectSize() 获取浅层大小
这是JDK自带、最轻量的获取方式,但只返回“浅大小”(shallow size):对象自身占用,不含引用指向的其他对象。
- 必须通过Java Agent启动,无法在普通main方法里直接调用
- 需要写一个
premain方法,并打包成jar,用-javaagent:xxx.jar启动 - 返回值是近似值,对数组、内部类、某些动态生成类可能不准
- 不区分对象头是12字节还是8字节,也不反映GC后可能的压缩效果
示例关键代码:
public class SizeAgent {
private static volatile Instrumentation inst;
public static void premain(String args, Instrumentation instrumentation) {
inst = instrumentation;
}
public static long sizeOf(Object obj) {
return inst == null ? -1 : inst.getObjectSize(obj);
}
}
用JOL(Java Object Layout)看完整内存布局
JOL比 Instrumentation 更透明,能打印字段偏移、对齐填充、对象头结构,适合调试和教学。
立即学习“Java免费学习笔记(深入)”;
- 依赖
org.openjdk.jol:jol-core,Maven引入即可,无需Agent -
ClassLayout.parseInstance(obj).toPrintable()输出人类可读的内存分布 - 支持
VM.current().details()查看当前JVM实际启用的选项(如是否压缩类指针) - 注意:JOL默认使用反射+Unsafe探查,某些安全策略或GraalVM原生镜像下不可用
例如:
System.out.println(ClassLayout.parseInstance(new ArrayList<>()).toPrintable());会显示对象头12字节、
elementData 引用占4或8字节、还有4字节填充对齐到16字节边界。
为什么两个工具结果经常不一致?
根本原因在于测量粒度和时机不同:
-
Instrumentation.getObjectSize()是JVM在GC堆分配时记录的“提交大小”,含对齐但不含GC碎片;JOL是静态分析类定义 + 运行时实例快照,更贴近物理布局 - 数组长度为0的
ArrayList,Instrumentation可能返回32字节,JOL可能显示24字节(取决于是否把空数组对象本身也算进去) - 开启
-XX:+UseCompressedOops时,引用字段从8字节变4字节,JOL能立刻反映,而Instrumentation结果也变,但你得确认JVM真开了这个参数 - 如果对象刚被创建还没经历GC,
Instrumentation返回的是“分配大小”;如果经历过一次Minor GC且晋升失败,大小可能因内存重排微变(极少见,但存在)
真正要压测或调优时,别只信单次测量值——得结合 jstat 看老年代增长速率,或者用 jmap -histo 看类实例总占比。对象大小只是拼图一角,字段冗余、集合扩容策略、缓存行伪共享,往往比单个对象多几个字节影响更大。










