JVM 压缩指针
oop(ordinary object pointer),即普通对象指针,是JVM中用于代表引用对象的句柄。
对于Java进程,在oop只有32位时,只能引用4G堆内存。因此,如果需要使用更大的堆内存,需要部署64位JVM。这样,oop为64位,可引用的堆内存就更大了。32位的对象引用占4个字节,而64位的对象引用占8个字节。也就是说,64位的对象引用大小是32位的2倍。带来的负面效果就是,对象引用占用了更多内存空间,占用更多内存空间意味着gc的次数更多,占用更多cpu时间。因此,使用64位jvm并不一定能立刻带来性能上的提升。
那么难道要放弃使用64位机器吗?答案是可以通过压缩指针解决这个问题,实现在64位机器上使用32bit的对象指针来引用超过4G(事实上是32G)的堆内存空间。设置参数-XX:-UseCompressedOops即可开启压缩指针,从java7开始当最大堆内存小于32G时即默认开启压缩指针。
指针压缩的原理:由于jvm对所有对象都进行了padding处理,所以对象的大小都是8 bytes的倍数,这也就意味着对象地址都是8的倍数,即oop的最后3位总是0,这给指针压缩带来了空间。oop可以增加3位至35bit,其最后3位为0,当保存oop时将其右移3位,只保存32bit信息,取出来时左移3位恢复,这样就可以引用2^(32+3)=2^35=32 GB的堆内存空间。位移操作带来的消耗可以忽略不计。[^4]

PS:
- ZGC中由于需要使用64bit colored pointers,因而不支持指针压缩,因此需要权衡它带来的更低的gc延迟与更多内存消耗。
- 在堆内存超出32GB的情况下,可以通过修改-XX:ObjectAlignmentInBytes参数改变对象padding对齐的大小,这个值必须是8到256之间的一个2的指数,来使用压缩指针。例如当设为按16bytes对齐时,可通过压缩指针管理64GB的堆内存。带来的问题时,对象之间的空闲空间增多,因而实际可能并没有性能上的提升。