zoukankan      html  css  js  c++  java
  • JVM之指针压缩&内存如何设置

      在32位到64位的转变中,人们最大的获益是内存容量。在一个32位的系统中,内存地址的宽度就是32位,这就意味着,我们最大能获取的内存空间是2^32(也就是4G)字节。这个容量明显不够用!在一个64位的机器中,理论上,我们能获取到的内存容量是2^64字节,这是一个十分庞大的数字。可惜的是,这只是一个理论值,而现实中,因为有一堆有关硬件和软件的因素限制,我们能得到的内存要少得多。举个例了来说,最好的 linux 系统最多支持到16TB的内存,而且截止到现在 java11 也在正在研制最新一代的垃圾收集器 ZGC ,号称可以支持到 TB 几倍的且能保证 STW 时间不会太长,可能许多人会说“16TB好大呀”,但是和2^64比起来,它真的挺小的。好了,接下来,我们就谈谈compressed oops能帮我们做什么。

    什么是 OOP ?

      在堆中,32位的对象引用(指针)占4个字节,而64位的对象引用占8个字节。64位JVM在支持更大堆的同时,由于对象引用变大却带来了性能问题:

      1. 增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC。
      2. 降低CPU缓存命中率:64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。

      为了能够保持32位的性能,oop必须保留32位。那么,如何用32位oop来引用更大的堆内存呢?答案是——压缩指针(CompressedOops)。

      OOP = “ordinary object pointer” 普通对象指针。 启用CompressOops后,会压缩的对象:

        1. 每个Class的属性指针(静态成员变量)

        2. 每个对象的属性指针

        3. 普通对象数组的每个元素指针

      当然,压缩也不是万能的,针对一些特殊类型的指针,JVM是不会优化的。 比如指向 PermGen的Class 对象指针,本地变量,堆栈元素,入参,返回值,NULL指针不会被压缩

    1)CompressedOops原理:

      JVM的实现方式是,不再保存所有引用,而是每隔8个字节保存一个引用。例如,原来保存每个引用0、1、2...,现在只保存0、8、16...。因此,指针压缩后,并不是所有引用都保存在堆中,而是以8个字节为间隔保存引用。

      CompressedOops,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价。 不过它的实现方式是在机器码中植入压缩与解压指令,可能会给JVM增加额外的开销。

    2)零基压缩优化(Zero Based Compressd Oops)

      零基压缩是针对 压、解压 动作的进一步优化。 它通过改变正常指针的随机地址分配特性,强制堆地址从零开始分配(需要OS支持),进一步提高了压解压效率。要启用零基压缩,你分配给JVM的内存大小必须控制在4G以上,32G以下。

    3)总结:

    • 如果GC堆大小在 4G以下,直接砍掉高32位,避免了编码解码过程;
    • 如果GC堆大小在 4G以上32G以下,则启用 UseCompressedOop;
    • 如果GC堆大小 大于32G,压指失效,使用原来的64位(所以说服务器内存太大不好......)。

    灵魂提问

      从以上结果中我们知道 32GB 以下是启用了 UseCompressedOop,但是当我们线上真正启动服务的时候直接设置 -Xmx=32GB 的时候很可能导致 CompressedOop 失效,那我们怎么确定当前环境下最大内存设置多大才且最大限度的使用内存才能启动 CompressedOop 呢?

      32G是个近似值,这个临界值跟JVM和平台有关。如果不想精确设置的话,31G是个绝对安全的数值,31G肯定默认开启compressed oops。我们可以通过增加JVM参数 -XX:+PrintFlagsFinal,验证UseCompressedOops的值,从而得知,到底是不是真的开启了压缩指针,还是压缩指针失效!
      那我们就手动来验证这个临界值:

      32G,即32*1024=32768M,刚好在范围[32760, 32770]中。而 32768m 验证后得到并没有开启压缩指针,所以可以从 32760 ~ 32768 之间验证选择一个最大的且开启压缩指针的内存大小。

    简单测试验证

      我们来做了一个简单测试,验证一下这个问题:分配设置内存大小分别为:-Xmx32760m 和 -Xmx32770m,-Xmn 都是 100M(S0:S1:Eden默认1:1:8)。总计分配一个包含8个对象类型和8个原子类型以及String,总计17个类型属性的对象 1kw 次。看它们分别触发了多少 YGC,结论如下表所示:
    实验次数    开启压缩指针YGC次数    关闭压缩指针YGC次数

       由执行结果可知,Young区完全一样的情况下,开启压缩指针相比关闭压缩指针,能节省20%多的内存。由此可知,32G还真是一个奇妙的魔法数值!另外需要说明的是YGC次数有小数,是表示Eden区占用比例,比如 17.52 次 YGC 表示发生了 17次 YGC 并且 Eden 还占了52%

  • 相关阅读:
    网页布局色块
    多物体运动
    elasticsearch基础命令
    mysiam,innodb
    git常用命令
    redis内存淘汰机制
    PHP运行模式
    MySQL主从延迟
    ip、uv、pv
    缓存出现的几种情况
  • 原文地址:https://www.cnblogs.com/liang1101/p/12727754.html
Copyright © 2011-2022 走看看