跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

Java中的对象布局(Object Layout)包含哪些部分_补齐填充与头部说明

Java对象头包含Mark Word和Class Pointer(普通对象)或额外Array Length字段(数组对象),其大小受压缩指针开关影响:开启时普通对象头12字节,关闭时16字节;空Object因需8字节对齐而占16字节。 对象头里到底存了啥 Java对象在堆内存里的布局,对象头是第一部分,它不是固定大小,而是分两种情况:普通对象和数组对象。普通对象头包含
Mark Word
和
Class Pointer
;数组对象多一个
Array Length
字段(4字节)。
Mark Word
会复用存储锁状态、GC分代年龄、哈希码等信息,具体存什么取决于当前锁状态——比如无锁时存哈希码,轻量级锁时存指向栈中锁记录的指针。 注意:开启
-XX:+UseCompressedClassPointers
(默认开启)时,
Class Pointer
占4字节而非8字节;但若堆大于32GB,压缩指针失效,它就变成8字节。这直接影响对象头总长,进而影响后续字段对齐。 64位 JVM + 压缩类指针 + 普通对象 → 对象头 = 8(Mark Word) + 4(Class Pointer) = 12 字节 64位 JVM + 关闭压缩类指针 → 对象头 = 8 + 8 = 16 字节 数组对象额外 + 4 字节
Array Length
字段对齐与补齐填充怎么算 JVM要求对象起始地址必须是8字节的整数倍,同时每个字段也要按自身宽度自然对齐(比如
long
和
double
要求8字节对齐)。字段按宽度从大到小排序后插入(HotSpot 实现),再在末尾补零,凑够8字节倍数。 举个例子:
class A { byte a; long b; int c; }
字段排布不是按声明顺序,而是先放
b
(8字节),再放
c
(4字节),再放
a
(1字节),最后补7字节填充 → 总大小 = 8 + 4 + 1 + 7 = 20 字节,但对象总大小必须是8的倍数,所以最终是24字节。 立即学习 “ Java免费学习笔记(深入) ”; 字段重排序只发生在对象实例字段之间,不包括父类字段(父类字段在前,子类字段在后) 静态字段、常量池、方法区内容不参与对象布局计算 使用
-XX:+PrintFieldLayout
可直接打印 HotSpot 的字段排布结果,比手动算靠谱 为什么
java.lang.Object
占16字节 空对象也要满足对齐要求。
Object
没有实例字段,只有对象头。在默认压缩指针下,对象头共12字节(8+4),但12不是8的倍数,所以 JVM 在末尾加4字节填充 → 总大小16字节。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 这个“16字节”是常见误区来源:有人以为是头占16,其实是头+填充。一旦你加一个
byte
字段,对象大小不会变成17,而是仍为16(因为
byte
塞进填充区),直到填满才扩容。
new Object()
→ 16 字节
class B { byte x; }
→ 还是 16 字节(x 填进那4字节空隙)
class C { byte x; long y; }
→ 24 字节(y 需要8字节对齐,前面12字节头 + 1字节x + 3填充 + 8字节y = 24) 排查布局问题时最容易忽略的点 对象大小不是靠
sizeof
或反射能直接拿到的,必须用工具。JOL(Java Object Layout)是事实标准,但要注意它的输出依赖 JVM 参数,尤其是是否启用压缩指针、是否开启分层编译等。 另一个坑是:对象数组(
Object[]
)本身是对象,其元素引用是4或8字节,但引用指向的每个
Object
实例又各自占16字节——这里容易把“数组对象大小”和“数组里所有元素总内存”混为一谈。 别信 IDE 的“估算大小”,它不考虑对齐和填充
Instrumentation.getObjectSize()
返回的是 shallow size(不含引用对象),且不反映实际内存布局细节 用 JOL 看布局前,先确认 JVM 启动参数和目标 JDK 版本,不同版本字段排序策略可能微调 对象布局真正麻烦的地方不在结构本身,而在它被 JVM 实现细节、CPU 缓存行、GC 算法共同牵制——改一个字段类型,可能让填充变多、缓存局部性变差、GC 扫描成本上升。这些影响往往藏在压测数据里,而不是日志里。

相关文章