1、将Java Heap Size设置的大于32G会对性能有什么影响?
开门见山的说,结果有几点(这几点其实也是内部关联):
-
触发JVM的临界值,优化策略Compressed OOPS失效(之前Heap Size在[4G~32G]区间内采用此优化)
-
由于优化策略失效,同时堆内存>32G,所以JVM被迫使用8字节(64位)来对Java对象寻址(之前4字节(32位)就够了)
-
通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(事实上当内存到达40-50GB的时候,有效内存才相当于使用Compressed OOPS技术时候的32G内存)
-
更大的指针在主内存和缓存器(例如LLC, L1等)之间移动数据的时候,会占用更多的带宽
-
让JVM的GC面临更大压力的指针对象(在实际应用中构建大于12-16G的堆时,若无很好的性能调优与测评,你很容易就会引起一个耗时数分钟的完全GC)
1.1 JVM的OOPS
OOP = “ordinary object pointer” 普通对象指针
启用CompressOops后,会压缩的对象:
-
每个class的属性指针(静态成员变量)
-
每个对象的属性指针
-
普通对象数组的每个元素指针
1.2 JVM的优化策略Compressed OOPS
从JDK 1.6 update14开始,64 bit JVM正式支持了 -XX:+UseCompressedOops ,这个可以压缩指针,起到节约内存占用的新参数。
Compressed OOPS,即大雾的对象压缩技术,压缩引用到32位,以降低堆的占用空间。其伪代码原理就不贴了,
在堆大小在[4G~32G]的时候,这项技术会被触发,在JVM执行时加入编/解码指令,即
JVM在将对象存入堆时编码,在堆中读取对象时解码
内存地址确定公式类似于
1
<narrow-oop-base(64bits)> +(<narrow-oop(32bits)><< 3) +<field-offset>
Zero Based CompressedOOPS(零基压缩优化)则进一步将基地址置为0(并不一定是内存空间地址为0,只是JVM相对的逻辑地址为0,如可用CPU的寄存器相对寻址) 这样转换公式变为:
1
(<narrow-oop << 3) +<field-offset>
从而进一步提高了压解压效率。
使用Zero Based CompressedOOPS后,它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。
1.3 Zero Based CompressedOOPS的多种策略
它可以针对不同的堆大小使用多种策略,具体可以 ps + grep查看:
-
堆小于4G,无需编/解码操作,JVM会使用低虚拟地址空间(low virutal address space,64位下模拟32位)
-
小于32G而大于4G,使用Zero Based CompressedOOPS
-
大于32G,不使用CompressedOOPS
2、结论
-
Compressed OOPS,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价
-
它的实现方式是在机器码中植入压缩与解压指令,可能会给JVM增加额外的开销