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

直接减少对象头和引用字段的字节数
开启 UseCompressedOops 后,JVM 把原本 8 字节的对象引用(Oop)和类元数据指针(Klass Pointer)统一压缩为 4 字节。这不是靠算法压缩,而是利用对象在堆中强制 8 字节对齐的特性——地址低 3 位恒为 0,所以只存高位部分(右移 3 位),用 32 位就能覆盖 ≤32GB 堆空间。
例如一个简单类:class Person { String name; int age; }
- 未压缩:对象头 16 字节(MarkWord 8 + KlassPtr 8) + name 引用 8 字节 + age 4 字节 + 对齐填充 4 字节 = 32 字节
- 开启压缩:对象头 12 字节(MarkWord 8 + KlassPtr 4) + name 引用 4 字节 + age 4 字节 + 对齐填充 4 字节 = 24 字节
单个对象节省 8 字节,百万实例就省约 8MB 堆内存。
影响范围明确:哪些指针被压,哪些不参与
被压缩的包括:
- 对象头中的 Klass Pointer(指向类元数据)
- 实例字段里的对象引用(如
List<item> items</item>) - 对象数组(
Object[])的每个元素引用 - 静态变量中持有的对象引用
不压缩的包括:
- 局部变量、方法参数、返回值中的引用(存在于栈帧,不走 Oop 压缩路径)
- 指向元空间 Class 对象的特殊指针(如类加载器相关元数据)
- NULL 指针(直接编码为 0,无需解压)
对齐填充可能间接变化
压缩后整体大小变小,可能改变是否需要填充字节。比如原对象占 28 字节(需补 4 字节到 32),压缩后占 20 字节,仍需补 4 字节到 24;但若压缩后刚好是 8 的倍数(如 16、24),填充量就减少甚至为 0。虽然填充本身不固定,但压缩显著降低了“因引用膨胀导致必须多填”的概率。
大堆场景下的行为边界
-Xmx ≤ 4GB:JVM 通常关闭压缩,直接用 32 位寻址,零开销
4GB < -Xmx ≤ 32GB:默认启用,收益最明显,堆内存可降低 30%~50%,GC 频率下降
-Xmx > 32GB:自动禁用 UseCompressedOops(除非显式调大对齐,如 -XX:ObjectAlignmentInBytes=16,但会增加填充浪费);此时对象引用和 Klass Pointer 都恢复为 8 字节,对象体积明显增大
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











