跳转到主内容
极星编程网:以代码为星,赴技术山海!

怎么通过分析 JVM 的对象布局(Object Layout)理解 8 字节对齐对读取性能的提升

对象必须8字节对齐,是因为64位CPU以8字节为单位读取数据,未对齐会导致跨缓存行访问,降低性能甚至引发硬件异常;同时保障long、double、volatile字段的原子性及指针压缩(Compressed Oops)正确工作。 为什么对象必须是 8 字节 对齐 64 位 JVM 中,CPU 一次能读取 8 字节(64 位)数据,这是硬件层面的自然单位。如果对象起始地址不是 8 的倍数,比如落在偏移
3
处,那读一个
long
字段(8 字节)就会横跨两个缓存行——x86 虽然允许,但会显著降速;ARM 或 RISC-V 架构可能直接抛
SIGBUS
。JVM 强制 8 字节对齐,本质是让每个
long
、
double
、对象头、压缩指针等关键结构都能被单次原子读取,避免未对齐访问带来的性能惩罚或正确性风险。 用
jdk.internal.vm.annotation.Contended
和
Unsafe
验证对齐效果 实际验证时别只看
java.lang.instrument.Instrumentation.getObjectSize()
返回的“总大小”,它不反映字段真实偏移。要用
jol-cli.jar
工具:
java -jar jol-cli.jar internals java.lang.Long
输出中重点关注
OFFSET
列:
Long.value
的偏移是
16
(8 的倍数),不是紧挨着对象头(12 字节)之后的
12
。这说明 JVM 主动插入了
4
字节填充(
alignment/padding gap
),只为把
value
对齐到 8 字节边界。这个填充不是浪费,而是保障后续任意字段访问都不越界。 指针压缩(Compressed Oops)和 8 字节对齐的强绑定关系
CompressedOops
能生效,前提是所有对象地址末 3 位二进制恒为
000
——这正是 8 字节对齐的数学结果。JVM 利用这点,把原本 8 字节的原生指针压缩成 4 字节,再在运行时左移 3 位还原。一旦你用
-XX:ObjectAlignmentInBytes=16
改成 16 字节对齐,末 4 位都为 0,理论上可进一步压缩,但 HotSpot 当前不支持;而设为
4
会破坏
long
/
double
的原子性要求,JVM 启动直接报错
Invalid ObjectAlignmentInBytes
。 byte-units-master转换字节单位的PHP库 byte-units-master转换字节单位的PHP库 下载 字段排列顺序如何受 8 字节对齐驱动 字段不是按声明顺序排布的,而是按类型宽度降序重排:先放所有
long
/
double
(8 字节),再
int
/
float
(4 字节),最后
short
/
byte
/
boolean
(1–2 字节),引用类型(4 或 8 字节,取决于是否启用压缩)。这么做是为了减少填充字节数。例如:
class A { int a; long b; byte c; }
实际布局是:
b
(偏移 0)→
a
(偏移 8)→
c
(偏移 12)→ 填充 4 字节 → 总大小 16。若按声明顺序排,
a
占 0–3,
b
就得从 4 开始,导致跨 8 字节边界,触发额外填充,总大小变成 32。 真正容易被忽略的是:对齐不只是为了“快”,更是为了“正确”。volatile
long
字段的原子写入、CAS 操作、GC 扫描对象图时的指针遍历,全部依赖地址对齐。一旦手动绕过(比如用
Unsafe
写入非对齐偏移),轻则性能归零,重则 JVM 崩溃。

相关文章