Java对象内存布局严格分为对象头、实例数据和对齐填充三部分:对象头含Mark Word和类型指针,实例数据按字段宽度重排序以减少填充,对齐填充确保总大小为8字节倍数,从而提升CPU缓存访问效率并缓解伪共享。

Java 对象在内存中的对齐不是靠“计算公式”硬套,而是由 JVM 按固定规则逐步安排出来的。核心就三点:字段重排、字段对齐、整体补齐。下面分步讲清楚怎么一步步得出最终占用大小。
字段按类型大小重排序
JVM 不按你代码里声明的顺序放字段,而是把宽的字段优先往前排,常见排序是: - long / double(8 字节) - int / float(4 字节) - short / char(2 字节) - byte / boolean(1 字节) - 引用类型(4 字节或 8 字节,取决于是否开启压缩指针)
比如这个类:
class A {
byte b;
int i;
long l;
}
实际布局顺序会变成 l → i → b,而不是 b → i → l,目的是减少中间填充。
每个字段起始位置必须对其自身宽度对齐
- byte / boolean:起始地址 % 1 == 0(天然满足) - short / char:起始地址 % 2 == 0 - int / float:起始地址 % 4 == 0 - long / double / 引用(64位未压缩):起始地址 % 8 == 0
例如:byte b; int i; 中,b 占 1 字节,偏移为 0;i 要从 4 开始(因需 % 4 == 0),中间自动补 3 字节填充。
整个对象大小必须是 8 字节倍数
这是硬性要求,不管内容多少,最后都要补齐到 8 的整数倍。补齐部分叫“对齐填充”,纯占位,无数据含义。 计算方式: 总大小 = 对象头 + 实例数据区域长度 + 填充字节 其中: - 64 位 JVM + 开启压缩指针:普通对象头 = 12 字节(Mark Word 8B + Klass Pointer 4B) - 数组对象头 = 16 字节(+ length 4B) - 实例数据区域长度 = 所有字段按重排后+填充后的累计偏移 - 填充字节 = (8 − (对象头 + 实例数据) % 8) % 8
举个例子:new Object()
- 对象头 = 12B
- 实例数据 = 0B
- 12 % 8 = 4 → 需补 4B → 总大小 = 16B
再如:class X { byte a; long b; }
- 重排后:
b(8B, 偏移 0) →a(1B, 偏移 8) - 实例数据区域长度 = 8 + 1 = 9
- 对象头 12B + 实例数据 9B = 21B → 21 % 8 = 5 → 补 3B → 总大小 = 24B
对齐不只是省空间,更是为 CPU 缓存服务
8 字节对齐直接适配主流 CPU 缓存行(64 字节 = 8×8 字节)。若不对齐,一个字段跨两个缓存行,读写效率下降;多个线程修改同一缓存行里的不同字段,还会引发伪共享,严重拖慢并发性能。所以这不是“浪费内存”,而是用少量 padding 换取显著的访问效率和线程安全收益。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











