
在本文中,我们将讨论 JEP 519 以及它为 Java 25 版本带来的重大改进。这个"紧凑对象头(Compact Object Headers)"JEP 旨在将紧凑对象头从实验版本转变为 HotSpot JVM 运行时中的完整产品功能。
JEP 450 是 JEP 519 的前身,它将紧凑对象头引入了 Java 24。JEP 450 的紧凑对象头功能在减少 Java 应用程序的内存开销和提高性能方面发挥着至关重要的作用。
在本文中,我们将主要关注 Java 中的自定义对象头及其影响。
JEP 519 在 JDK 25 中引入了紧凑对象头作为替代的对象头布局。这个 JEP 的主要目标是在 64 位架构上的 Hotspot JVM 中减少对象头的大小。
这个 JEP 的主要目标是将对象头大小从 96 或 128 位减少到 64 位(8 字节)在 64 位平台上。
对于平均 256-512 位的 Java 对象,对象头占总内存占用量的 20% 以上。因此,这个 JEP 带来了实质性的好处,比如:
在 HotSpot JVM 中,Java 对象在其内存布局的开头都有一个强制性的头。这个头由两个组件组成:标记字(marker word)和类指针。
标记字是一个 64 位字段,表示实例特定的元数据,包括:
我们可以在表格中看到这一点:
位 (63-0) | 大小 (位) | 用途 |
|---|---|---|
63 – 38 | 26 | 未使用/保留(可用于偏向锁定时epoch、对象地址等) |
37 – 7 | 31 | 身份哈希码 |
6 – 3 | 4 | GC 年龄(用于 G1 和 Parallel GC 等分代收集器) |
2 – 0 | 3 | 锁定状态/标志(对同步至关重要) |
在 64 位系统上,JVM 将类指针存储为 32 位压缩引用,并用它来定位元空间中的类元数据。这种设计让 JVM 能够高效地执行反射和类型检查。
如果对象是数组支持的引用,还会有第三个字段用于数组长度信息。
因此,可以推断出在 64 位系统上对象头的大小大约是 96 到 128 位。
随着 JEP 450 的推出,提出了一种新的紧凑头布局。这个改变的目标是减少对象头的平均大小。
紧凑对象头通过将类指针以压缩形式合并到标记字中,消除了标记字和类指针之间的划分。
因此,生成的 64 位紧凑对象头包含以下字段:
使用紧凑对象头应该启用压缩类指针。此外,这种紧凑布局通过改变编码方案将类指针大小从 32 位减少到 22 位。
在接下来的章节中,让我们更深入地了解这个功能的技术实现细节。
轻量级锁现在通过改变对象头中的几个微小位来工作,以显示锁是被占用还是空闲。 只有那些特定的位被更新,头中的其他内容都不会被改变或移动。
同样,重量级锁(监视器),用于 wait/notify 场景中,需要创建监视器结构,但只调整头的标记位。 在新布局中,所有其他位都被保留。
消失的是旧的栈锁定路径;紧凑对象头根本不再支持那个传统机制。
Hotspot 收集器(不包括 ZGC)现在通过在对象头中写入转发指针来重定位对象,并且只保留一些头。
随着压缩类指针的引入,引入了一种新的编码方式。使用这种编码,自转发不会破坏头数据,相反,一个特定的位表示自转发,转发地址适合放入较低的 42 位中。
最后,完整的(滑动)GC,必须保留每个头,被重新设计以利用新的打包方案。
压缩类指针现在已经成为遵循紧凑头方案的强制性要求。 这将类数量限制在 400 万以下,尽管这对于所有 Java 应用程序来说都是足够的。
一些仍然不支持这种布局的 JIT 编译器,作为保护机制会自动禁用此功能。
紧凑对象头之前是一个实验性功能,因此默认是禁用的。我们可以使用以下 JVM 标志启用紧凑对象头:
JEP 519 将这个功能移出了实验阶段。 我们可以使用 JVM 标志 -XX:+UseCompactObjectHeaders 启用这个产品功能。
通过这个改变,对象头大小缩小到 8 字节。因此,这使得 JVM 能够显著减少堆中每个对象的占用空间。
拥有许多小对象的应用程序的总内存占用量将显示实时数据使用的总内存减少 10-20%。 因此,更低的内存消耗意味着硬件效率、GC 效率,以及对云和容器化环境更好的部署密度。
紧凑对象头导致相同的 Java 工作负载使用更少的总堆空间,从而导致更不频繁和更快的垃圾收集。这也导致减少的 GC 活动,这在内部有助于降低延迟和提高吞吐量,特别是在分配繁重的工作负载中。
数据局部性 用于定义相关数据片段在内存中彼此靠近放置的程度。JVM 使用数据局部性来优化内存效率。
随着对象头的新布局,整体对象大小减小。这改善了缓存行利用率和数据局部性,因为更多对象可以适合单个 CPU 缓存行。 因此,它允许 JVM 以更少的缓存未命中遍历、操作和移动对象。
随着头位密度的增加和新的头编码,存在未来 JVM 功能可能用完头位的风险。这个功能还需要广泛的测试和基准测试,以验证在并发负载下的可靠性和性能。此外,x64 上的 JVMCI 不支持这种紧凑头结构。 当启用 JVMCI 时,通过关闭该功能来缓解风险。
长期风险是如果 JVMCI 从不实现对紧凑头的支持,这将永远阻止传统头实现的迁移。一些直接操作对象头的组件,特别是作为 JVMCI 主要用户的 Graal 编译器,将不得不实现新的头布局。因此,在这种情况下该功能被禁用。
最后,我们将讨论与项目失败相关的风险。虽然非常不可能,但存在这个功能与传统头结构相比有不可调和的功能回归的可能性。 这可能是因为可表示类的数量总体减少。另一个高度不可能的结果可能是紧凑对象头没有产生有形的现实世界改进,或者升级不能证明整体复杂性增加是合理的。
虽然没有启用这个对象头实现的默认举措,但几个基准测试结果讨论了这个功能的优势和好处:
在本文中,我们深入讨论了 JEP 519 的紧凑对象头功能。这个功能旨在通过减少对象头大小在性能和空间效率方面提供显著的增益。
我们还深入研究了这个功能是如何内部化的,以及对锁定和 GC 转发头信息所做的修改。总的来说,这个功能使 JVM 为未来做好了准备。 翻译:https://www.baeldung.com/java-object-header-reduced-size-save-memory