当前位置: 首页 > 工具软件 > 171cms > 使用案例 >

CMS的CMSInitiatingOccupancyFraction 及 触发条件

赵元白
2023-12-01

CMS触发条件:https://blog.csdn.net/a479898045/article/details/96074872

使用例子:
-XX:CMSInitiatingOccupancyFraction=70
CMS垃圾收集器,当老年代达到70%时,触发CMS垃圾回收。

查看CMSInitiatingOccupancyFraction的初始值为-1

intx CMSInitiatingOccupancyFraction            = -1                                  {product}

那么-1代表着什么呢?

查看jvm源码可知

product(intx, CMSInitiatingOccupancyFraction, -1,                         \
          "Percentage CMS generation occupancy to start a CMS collection "  \
          "cycle. A negative value means that CMSTriggerRatio is used")   

注释里也说了,如果CMSInitiatingOccupancyFraction是个负值,那么CMSTriggerRatio将被用到

那么具体是如何用到的呢?

_cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction, CMSTriggerRatio);

void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) {
  assert(io <= 100 && tr <= 100, "Check the arguments");
  if (io >= 0) {
    _initiating_occupancy = (double)io / 100.0;
  } else {
    _initiating_occupancy = ((100 - MinHeapFreeRatio) +
                             (double)(tr * MinHeapFreeRatio) / 100.0)
                            / 100.0;
  }
}

如果CMSInitiatingOccupancyFraction在0~100之间,那么由CMSInitiatingOccupancyFraction决定。

否则由按 ((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0 决定。

那么MinHeapFreeRatio,CMSTriggerRatio的初始值是多少?

uintx MinHeapFreeRatio                          = 40                           {manageable}
uintx CMSTriggerRatio                           = 80                                  {product}

即最终当老年代达到 ((100 - 40) + (double) 80 * 40 / 100 ) / 100 = 92 %时,会触发CMS回收。

----------------------------------------------------------------------------------------------------------------------------------------------------------------

经常看到有人谈到CMSInitiatingOccupancyFraction这个CMS相关的JVM启动参数,但有的人说默认值是68,有的人又说是默认92,还有人干脆丢出了一个计算公式让你自己去算。

众口不一,这鬼知道到底哪个说的对。

后面就自己在openjdk里面找了下CMS的相关实现,最终找到了用来确定CMSInitiatingOccupancyFraction的这个方法:
ConcurrentMarkSweepGeneration.cpp:

  void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) {
      assert(io <= 100 && tr <= 100, "Check the arguments");
      if (io >= 0) {
        _initiating_occupancy = (double)io / 100.0;
      } else {
        _initiating_occupancy = ((100 - MinHeapFreeRatio) +
                                 (double)(tr * MinHeapFreeRatio) / 100.0)
                                / 100.0;
      }
    }


其中两个参数,io就是CMSInitiatingOccupancyFraction的值,如果你没设置过就是虚拟机自己的默认值,默认-1,可以用指令来查看:

java -XX:+PrintFlagsFinal -version |grep CMSInitiatingOccupancyFraction
1
tr就是JVM启动参数CMSTriggerRatio的值,没手动设置过的话也同样也可以用指令来查看默认值:

java -XX:+PrintFlagsFinal -version |grep CMSTriggerRatio
1
这个方法的逻辑很简单,就是CMSInitiatingOccupancyFraction大于等于0就走上面分支,否则走下面分支。

如果你自己设置过-XX:CMSInitiatingOccupancyFraction=70,那么最终_initiating_occupancy=70%。在你设置了-XX:+UseCMSInitiatingOccupancyOnly的情况下,老年代内存使用率一旦超过70%就会执行CMS GC了。

如果你没设置过CMSInitiatingOccupancyFraction,默认值-1就走下面分支了。在该分支下还用到一个启动参数:MinHeapFreeRatio。emmm,怎么看值是啥不用我多说吧:

  java -XX:+PrintFlagsFinal -version |grep MinHeapFreeRatio
1
其实省事点也可以一次查看这三个启动参数的值:

java -XX:+PrintFlagsFinal -version |grep -E "CMSInitiatingOccupancyFraction|MinHeapFreeRatio|CMSTriggerRatio"
1
我分别在JDK1.7(1.7.0_67-b01)和JDK1.8(1.8.0_171)下看了这三个参数的值:
CMSInitiatingOccupancyFraction和CMSTriggerRatio都是-1和80。
但是MinHeapFreeRatio在JDK1.7是0,所以根据公式计算_initiating_occupancy最终得到的值是100%。
而MinHeapFreeRatio在JDK1.8是40,根据公式_initiating_occupancy计算得到的值是92%(68%的可能是其他版本计算得到的值了~)

大家也可以自己试试,看看自己JVM版本中MinHeapFreeRatio值是多少,最终计算得到的_initiating_occupancy又是多少。

最后来总结一下吧:
1、CMSInitiatingOccupancyFraction默认值是-1,并不是许多人说的68、92之类的。

2、CMSInitiatingOccupancyFraction是用来计算老年代最大使用率(_initiating_occupancy)的。大于等于0则直接取百分号,小于0则根据公式来计算。这个_initiating_occupancy需要配合-XX:+UseCMSInitiatingOccupancyOnly来使用。

3、不同版本最终得到的_initiating_occupancy不同,归根结底应该是不同版本下的MinHeapFreeRatio值不同。

 类似资料: