JVM指针压缩实现原理
概要
Java 中的指针压缩(Pointer Compression)是一个与内存管理相关的优化技术,主要应用于 JVM 的对象引用(即指针)的存储方式。指针压缩的目标是减少对象引用占用的内存空间,从而提高内存利用效率,特别是在 64 位系统上。
一、对象的内存布局
在了解指针压缩之前,需要先搞懂java的实例对象在JVM虚拟机中内存结构是什么样的。
在Hotspot虚拟机中,对象在内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
1. 对象头(Header)
对象头,又包括三部分:MarkWord、元数据指针、数组长度。
下面图是64位系统下的对象头布局
MarkWord:用于存储对象运行时的数据,比如HashCode、锁状态标志、GC分代年龄等。这部分在64位操作系统下,占8字节(64bit),在32位操作系统下,占4字节(32bit)。
具体的作用可以参考我之前写的synchronized锁原理分析(一、从Java对象头看synchronized锁的状态)
指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
这部分就涉及到一个指针压缩的概念,在开启指针压缩的情况下,占4字节(32bit),未开启情况下,占8字节(64bit),现在JVM在1.6之后,在64位操作系统下都是默认开启的。
数组长度:这部分只有是数组对象才有,如果是非数组对象,就没这部分了,这部分占4字节(32bit)。
2、实例数据(Instance Data)
用于存储对象中的各种类型的字段信息(包括从父类继承来的)。
3、对齐填充(Padding)
java对象的大小默认是按照8字节对齐,也就是说java对象的大小必须是8字节的倍数。如果算到最后不够8字节的话,那么就会进行对齐填充。
比如:对象头+对象体大小不是8字节的倍数,则通过该部分进行补齐,比如对象头+对象体大小只有30字节,则需要补齐到32字节,这里的对齐填充就是2字节。
二、指针压缩
1. 为什么要指针压缩
计算机操作系统分32位和64位,这里的位在计算机里是用0和1来表示的,用32个(或64个)二进制0和1的组合来表示内存地址。以32位为例,在普通的内存中,对象的大小最小是以1字节来计算的,通过0和1的排列组合,能够表示寻址的内存空间最大就是2^32个,换算成内存空间就是2 ^ 32 / 1024 / 1024 / 1024 = 4G,也就是说32位的操作系统最大能寻址的内存空间只有4G。同理,64位的操作系统最大能寻址的内存空间就更大了。
但是当我们准备将32位系统切换到64位系统,起初我们可能会期望系统性能会立马得到提升,但现实情况可能并不是这样的,为什么呢?
1)增加了GC开销
64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC。
2)降低CPU缓存命中率
64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。
既然32位系统内存不够,64位内存够但又影响性能,那有没有折中方案来解决这两个问题呢,于是聪明的JVM开发者想到了利用压缩指针,在64位的操作系统中利用32位的对象指针引用获得超过4G的内存寻址空间。
2. 如何压缩指针
在 JVM 中,对象的内存地址总是 8 字节对齐,即每个对象的地址都是 8 的倍数,这意味着其二进制表示的末尾 3 位始终为 0。指针压缩正是利用了这一特点,省略了这些无意义的 0 位:
压缩存储:使用 32 位来存储指针(而非 64 位),省去末尾的 3 个 0,从而节省空间。
地址还原:当 JVM 需要访问对象时,通过将这个 32 位指针左移 3 位来恢复完整的 64 位地址。
这种方式扩展了可寻址的内存空间,使得 32 位指针可表示 2^35(约 32GB)的内存地址,从而在 64 位系统上为 JVM 节省内存并满足大部分应用需求。
3. JVM指针压缩参数
关于压缩指针的两个参数:
UseCompressedClassPointers:压缩类指针
UseCompressedOops:压缩普通对象指针
Oops是Ordinary object pointers的缩写,这两个参数默认是开启的,即-XX:+UseCompressedClassPointers,-XX:+UseCompressedOops,也可手动设置,如下所示
2位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。
Oracle JDK从6 update 23开始在64位系统上会默认开启压缩指针。
三、总结
通过指针压缩,利用对齐填充特性,通过映射方式达到了内存地址扩展的效果
指针压缩能够节省内存空间,同时提高了程序的寻址效率
堆内存设置最好不要超过32GB,这时指针压缩将会失效,造成空间的浪费
指针压缩不仅可以作用于对象头的类型指针,还可以作用于引用类型的字段指针(包括引用类型的数组指针)
-XX:ObjectAlignmentInBytes,默认是 8,也就是 8 字节对齐
参考链接:
https://blog.csdn.net/liujianyangbj/article/details/108049482
https://www.cnblogs.com/star95/p/17512212.html