Java对象内存布局中,对象头含Mark Word(8字节)和Class Pointer(4或8字节),数组额外加4字节length;对齐填充确保总大小为8字节倍数,二者虽不存业务数据,却决定对象大小、内存占用与GC效率。

Java对象的内存布局里,对象头和对齐填充是两个容易被忽略但很关键的部分。它们不存业务数据,却直接影响对象大小、内存占用和GC效率。
对象头:元信息的“身份证”
对象头不是固定结构,而是随JVM配置和对象状态动态变化的区域,主要包含两块内容:- Mark Word(8字节):存放哈希码、GC年龄、锁标志位、偏向线程ID等。比如对象刚创建时没锁,这里可能存hashCode;一旦进入synchronized块,就可能替换成指向Monitor对象的指针。
- Class Pointer(4或8字节):指向类元数据(Klass结构)的地址。64位JVM默认开启指针压缩(-XX:+UseCompressedOops),所以是4字节;关闭后变成8字节。
- 数组额外字段(仅数组对象有,4字节):存储数组长度,普通对象没有这一项。
举个例子:new Object()在开启指针压缩的64位JVM中,对象头就是8 + 4 = 12字节。
对齐填充:凑整的“隐形占位符”
HotSpot要求每个对象在堆内存中的起始地址必须是8字节对齐的,也就是说,整个对象占用的总字节数必须是8的倍数。- 对齐填充本身没有语义,不存任何数据,只是补零用的空白字节。
- 它只在需要时才出现。比如对象头12字节 + 实例数据4字节 = 16字节,刚好是8的倍数,就不需要填充;但如果加起来是17字节,就得补7字节到24字节。
- 常见误区:认为对齐填充是对象的固定组成部分。其实它像“凑整税”,只看最终总大小是否满足8字节对齐。
怎么验证对象实际大小?
光靠理论推算容易出错,建议结合工具实测:- 使用
jol(Java Object Layout)库:添加依赖后调用GraphLayout.parseInstance(obj).totalSize(),能精确输出对象在当前JVM下的真实内存占用。 - 启动参数影响大:务必确认
-XX:+UseCompressedOops是否启用(默认开启),否则Class Pointer从4字节变8字节,对象头直接多出4字节。 - 注意区分“Shallow Size”和“Retained Size”:jol默认显示的是Shallow Size(对象自身占用,不含引用对象),这正是对象头+实例数据+对齐填充的总和。
为什么关注这两个部分?
- 内存敏感场景(如高频创建小对象、缓存设计)中,12字节对象头 + 4字节填充,就让一个空对象占16字节——比你想象中“重”得多。 - 锁优化(偏向锁、轻量级锁)全部依赖Mark Word的位布局,理解对象头才能读懂`synchronized`底层行为。 - 字段排列顺序会影响实例数据部分的对齐方式,间接改变是否需要更多填充字节,所以字段声明顺序也有优化空间。不复杂但容易忽略


















