为什么64位平台要使用压缩指针?
大家好,我是帅旋,今天来聊聊压缩指针的问题。
我们知道,在64位操作系统中,指针默认占用64位,相比于32位,消耗多了一倍的内存,如果能够将指针压缩到32位,那么就可以显著减少内存占用,提高缓存命中率,提升程序的性能。这对于内存密集型应用来说尤为重要,因为节省的内存可以用于存储更多的数据或对象。此外,较小的指针也可以减少CPU的缓存压力,加快数据访问速度。
压缩指针优势主要体现在:
减少内存消耗:
在64位系统中,未压缩的指针默认占用64位(8字节)。如果堆中存有大量的对象引用,这将显著增加内存的消耗。使用指针压缩,将指针大小减少至32位(4字节),可以减少每个对象引用消耗的内存量,从而降低整个应用的内存占用。
提高缓存利用率:
较小的指针大小意味着更多的引用可以被缓存在CPU的缓存中。这有助于提高数据的局部性,减少内存带宽的使用,从而在许多情况下提升应用的性能。
减轻垃圾收集(GC)压力:
使用较小的指针可以减少GC扫描和移动对象时所需处理的数据量。这不仅减轻了GC的压力,还可以提高GC的效率,尤其是在处理大量对象时。
为什么开启压缩指针还能只是32G内存寻址?
在Java的HotSpot JVM中,压缩指针(Compressed Oops)技术允许JVM使用32位的偏移量来引用堆中的对象,即使在一个大于4GB但小于或等于32GB的堆空间上。这种技术通过一个简单的算术运算(通常是乘法和加法)来实现,使得32位的偏移量能有效地映射到64位的实际地址空间。
压缩规则
堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间。
堆内存大于32G时,压缩指针会失效,会强制使用64位(8字节)来对java对象进行寻址,所以一般建议对内存设置为32G以内。
压缩参数
-XX:+UseCompressedOops:默认开启,压缩所有指针,ordinary object pointer。
-XX:+UseCompressedClassPointers:默认开启,压缩对象头里的类型指针Klass Pointer。
压缩原理
在压缩指针技术中,假设所有对象的起始地址都按一定的大小(如8字节)对齐。这意味着对象的实际地址的最后三个二进制位总是0(因为2^3=8),所以这三位可以在不丢失信息的情况下被省略。
省略这三位后,原本需要64位来存储的地址现在可以用61位来表示,如果堆大小小于32GB,实际上只需要35位就足够了(因为2^35=32GB)。
编码和解码过程:
- 编码(Compress):当JVM需要存储一个对象引用到32位的字段时,会将64位的实际地址右移三位(除以8),从而“压缩”这个地址。这种方式把原始的64位地址转换成了一个较小的值,通常称为“压缩的引用”或“偏移量”。
- 解码(Uncompress):当需要访问实际对象时,JVM取出这个32位的压缩引用,然后将其左移三位(乘以8),并加上一个基地址(Heap Base Address),这个基地址是在JVM启动时确定的,它表示压缩指针偏移量的起始点。