usecompressedoops将对象引用和klass指针从8字节压缩为4字节,利用对象8字节对齐使地址低3位恒为0、右移3位后32位可寻址≤32gb堆,单对象省8字节,百万实例省约8mb,堆内存降低30%~50%。

Java 对象通过指针压缩(UseCompressedOops)优化内存,核心是把原本 8 字节的对象引用和类元数据指针(Klass Pointer)压成 4 字节,不靠算法,而靠对象在堆中强制 8 字节对齐这一硬件友好特性——地址末三位恒为 0,右移 3 位后,32 位就能覆盖 ≤32GB 堆空间。
哪些地方被压缩了?
被压缩的指针包括:
- 对象头里的 Klass Pointer(指向类元数据)
- 实例字段中的所有对象引用(如
String name、List<item> items</item>) - 对象数组(
Object[])每个元素的引用 - 静态变量中持有的对象引用
不参与压缩的有:局部变量、方法参数、返回值中的引用(它们存在栈帧里,不走 Oop 路径),以及 native 层直接持有的指针、NULL 指针(直接编码为 0)。
一个 Person 对象能省多少?
以 class Person { String name; int age; } 为例:
- 未压缩:对象头 16 字节(Mark Word 8 + KlassPtr 8) + name 引用 8 字节 + age 4 字节 + 对齐填充 4 字节 = 32 字节
- 开启压缩:对象头 12 字节(Mark Word 8 + KlassPtr 4) + name 引用 4 字节 + age 4 字节 + 对齐填充 4 字节 = 24 字节
单对象省 8 字节;100 万个实例就少占约 8MB;亿级小对象场景下,轻松节省数 GB 堆内存。
堆大小怎么影响压缩是否生效?
JVM 不靠你加参数决定是否启用,而是看 -Xmx 自动判断:
-
-Xmx ≤ 4GB:默认关闭压缩,直接用 32 位寻址,零开销 4GB :默认启用,收益最大,堆内存可降低 30%~50%,GC 频次明显下降-
-Xmx > 32GB:自动禁用压缩(除非显式调大对齐,如-XX:ObjectAlignmentInBytes=16,但会增加填充浪费)
注意:设 -Xmx33g 却仍加 -XX:+UseCompressedOops,JVM 启动时会静默禁用,并在日志中提示 Compressed Oops is disabled。
顺便提一句对齐填充的变化
压缩后对象整体变小,可能改变是否需要填充字节。比如原来占 28 字节(补 4 字节到 32),压缩后变成 20 字节(补 4 字节到 24);但如果压缩后刚好是 8 的倍数(如 16、24),填充量就减少甚至为 0。虽然填充本身不固定,但压缩显著降低了“因引用膨胀被迫多填”的概率。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











