在网上大家很多都看到过这样一句话:“JVM内存最好不要超过32G”。 今天我们就来分析一下为什么?32G到底是怎么算出来的。
指针压缩原理
为什么对象指针,可以用8字节存也可以用4字节存?4字节存不会有什么问题吗?
1、不开启指针压缩
首先,我们来分析如果不开启指针压缩的情况下是,java对象是怎么存储的
采用8字节(64位)存储真实内存地址,比之前采用4字节(32位)压缩存储地址带来的问题:
增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少, 从而加快了GC的发生,更频繁的进行GC。
降低CPU缓存命中率:64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。
2、开启指针压缩
既然64位存储内存地址,会导致了这么多问题,那么我们可以不可以找一种方法,既使用之前的4字节(32位)存指针地址,又可以扩大内存的方法呢?
答案就是采用指针压缩技术!!
4个字节,32位,可以表示232 个地址,如果这个地址是真实内存地址的话,那么由于CPU寻址的最小单位是byte,也就是 232 byte = 4GB。
如果内存地址是指向 bit的话,32位的最大寻址范围其实是 512MB,但是由于内存里,将8bit为一组划分,所以内存地址就其实是指向的8bit为一组的byte地址,所以32位可以表示的容量就扩充了8倍,就变成了4GB。
上面这个原理一定要明白,才能理解下面的指针压缩原理,不明白可以先看一下我的这篇博客32位和64位CPU与内存之间的关系。
4字节,8位最大表示4GB内存。那么Java是怎么做到 4个字节表示32GB呢?怎有扩大了8倍???
这就要使用到之前提到的Java的对齐填充机制了。 Java的8字节对齐填充,就像是内存的8bit为一组,变为1byte一样。 这里的压缩指针,不是真实的操作系统内存地址,而是Java进行8byte映射之后的地址,所以也相对于操作系统的指针有进行的8倍的扩容。
看下图: JVM就将堆内存进行了块划分,以8字节为最小单位进行划分
将java堆内存进行8字节划分
java对象的指针地址就可以不用存对象的真实的64位地址了,而是可以存一个映射地址编号。 这样4字节就可以表示出2^32个地址,而每一个地址对应的又是8byte的内存块。 所以,再乘以8以后,一换算,就可以表示出32GB的内存空间。
这里很巧妙的运用了java对齐填充的特性,通过映射的方式达到了内存扩充的效果。
想一想?这里运用的原理是不是和操作系统32位表示4GB内存的原理一毛一样!!! 我想着也是java做对齐填充的一重大原因吧!!
也就解释了为什么当内存大于32GB时,开启指针压缩的参数会失效! 所以也网上建议大家在64位系统系下,JAVA的堆内存设置最好不要超过32G,一旦超过32G后,指针压缩就会失效,然后带来GC的触发频次变高,而且造成空间浪费。
注意:
32G是个近似值,这个临界值跟JVM和平台有关,当我们线上真正启动服务的时候直接设置 -Xmx=32GB 的时候很可能导致 CompressedOop 失效,那我们怎么确定当前环境下最大内存设置多大才且最大限度的使用内存才能启动 CompressedOop 呢?我们可以通过增加JVM参数 -XX:+PrintFlagsFinal,验证UseCompressedOops的值,从而得知,到底是不是真的开启了压缩指针,还是压缩指针失效!
总结:
- 如果GC堆大小在4G以下,直接砍掉高32位,避免了编码解码过程;
- 如果GC堆大小在4G以上32G以下,则启用UseCompressedOop;
- 如果GC堆大小大于32G,压指失效,使用原来的64位(所以说服务器内存太大不好)。
3、配置参数
jvm配置参数:UseCompressedOops,compressed--压缩、oop--对象指针, 启用指针压缩:-XX:+UseCompressedOops
禁止指针压缩:-XX:-UseCompressedOops
XX:+/-UseCompressedClassPointers:类指针压缩
UseCompressedClassPointers的开启是依赖于UseCompressedOops的开启,因此,要使UseCompressedClassPointers起作用,得先开启UseCompressedOops,并且开启UseCompressedOops 也默认强制开启UseCompressedClassPointers,关闭UseCompressedOops 默认关闭UseCompressedClassPointers。
复制代码
4、影响范围
-
哪些信息会被压缩? 对象的全局静态变量(即类属性)
对象头信息:64位平台下,原生对象头大小为16字节,压缩后为12字节
对象的引用类型:64位平台下,引用类型本身大小为8字节,压缩后为4字节
对象数组类型:64位平台下,数组类型本身大小为24字节,压缩后16字节 -
哪些信息不会被压缩?
指向非Heap的对象指针
局部变量、传参、返回值、NULL指针