关于-XX:MaxTenuringThreshold,我想大家肯定都不陌生。

-XX:MaxTenuringThreshold:Maximum value for tenuring threshold.即年轻代晋升老年代的最大年龄阈值。

关于这个参数,我想大部分人都知道的是默认值是15,而熟悉JVM的同学知道:

  1. 为什么最大是15.
  2. 在CMS下是6.
  3. 该参数只是初始值并会动态改变.

我以为这就是此参数的所有相关知识点了,没想到其实还有一些细节被我忽略了,<u>那就是CMS为6的情况也是只是默认情况而不是固定的</u>。

首先来简单解释下前几个point(基于openJDK1.8):

1.最大是15

这个很简单,age信息存放在对象头的Mark Word中,不论是32位还是64位,这个age都是4个bit,最大值二进制1111=十进制15.

2.在CMS下是6

这个我最初是在Oracle官网上看到的描述:

-XX:MaxTenuringThreshold=thresholdSets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector, <u>and 6 for the CMS collector</u>.

-XX:MaxTenuringThreshold=10
随后为了验证,我简单试验了一下: java -Xms50M -Xmx50M -Xmn40M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails test 然后使用jinfo查看相关的MaxTenuringThreshold:jinfo -flag MaxTenuringThreshold ‘你java应用的pid’ (可以使用jps来查看) cms默认 发现CMS下该值确实是6.(但是官方文档并没有说明为何CMS特殊是6,我当时的猜测是因为为了追求更短的STW,CMS的remark阶段因为要扫描整个GC Roots,而GC Roots是包含youngGen、oldGen在内的,晋升年龄为6可以使得youngGen得以快速晋升,从而大大减小remark阶段扫描youngGen带来的时间开销,有点像开启-XX:+CMSScavengeBeforeRemark,但Oracle对于数值的选择肯定有一个均衡). 这就是我昨天之前建立的认知了,但是昨天和一个朋友聊天时他说:他使用了CMS,没有手动设置MaxTenuringThreshold但是MaxTenuringThreshold却显示为15. 我当时第一感觉不阔能啊,难道官网骗我,CMS不应该是6吗?(我当即大声反驳说你嗦滴不对!)然后我把他的测试代码要过来一试 JVM参数如下:
-Xmx50M -Xms50m -Xmn40M  -XX:SurvivorRatio=6  -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintTenuringDistribution

(开启UseConcMarkSweepGC默认开启ParNew,PrintTenuringDistribution可以打印age信息)

代码如下:

    byte[] arr1 = new byte[5 * 1024 * 1024];
        arr1 = new byte[5 * 1024 * 1024];
        arr1 = new byte[5 * 1024 * 1024];
        arr1 = new byte[5 * 1024 * 1024];
        arr1 = new byte[4 * 1024 * 1024];
        arr1 = new byte[4 * 1024 * 1024];
        arr1=null;
        // 常驻对象
        byte[] arr2 = new byte[1 * 1024 * 1500];
        for (int i = 0; i < 14; i++) {
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;
            arr1 = new byte[5 * 1024 * 1024];
            arr1=null;  
    }
    //方便jinfo
    Thread.sleep(100000);

此时我jinfo发现:

jinfo1

我去!还真是15,再看打印信息:

log1

惊了,这是什么情况,和我的认知产生了冲突呀。
肿么办,赶紧去翻翻源码吧
翻了半天,我在arguments.cpp中找到了CMS和ParNew设置参数的函数
Arguments::set_cms_and_parnew_gc_flags(),在函数中我看到这么一句话

// Now make adjustments for CMS
intx   tenuring_default = (intx)6;
size_t young_gen_per_worker = CMSYoungGenPerWorker;

这分明就是6啊,wait,这个值好像是default啊,再往下翻

// Unless explicitly requested otherwise, definitely
// promote all objects surviving "tenuring_default" scavenges.
if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
FLAG_IS_DEFAULT(SurvivorRatio)) {
    FLAG_SET_ERGO(uintx, MaxTenuringThreshold, tenuring_default);
}

果不其然,官方文档中说的CMS的MaxTenuringThreshold为6,其实是在不手动设置参数MaxTenuringThreshold/SurvivorRatio的情况下默认是6,但是你一旦手动的显式的设置了这两者中的任意一者,那么最大晋升年龄就会变成你手动设置的值/15.

呜呜呜,刚才实验的参数去除-XX:SurvivorRatio=6再试一次:

-Xmx50M -Xms50m -Xmn40M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintTenuringDistribution

果然

jinfo2

log2

CMS不带MaxTenuringThreshold/SurvivorRatio的情况下才是6.新知识点Get.

这波啊,我身在第二层,以为看到了大气层,没想到其实看去了熔岩层.

3.参数值动态变化

JVM中还有另外一个参数:-XX:TargetSurvivorRatio,
这是期望survivor区存活大小的参数,默认是50,即50%。而在ageTable.cpp的动态计算方法ageTable::compute_tenuring_threshold中则使用到了这个参数进行计算desired_survivor_size:

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  while (age < table_size) {
    total += sizes[age];
    // check if including objects of age 'age' made us pass the desired
    // size, if so 'age' is the new threshold
    if (total > desired_survivor_size) break;
    age++;
  }
  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
    ...
}

简单的说即默认情况下如果某一年龄值的超过了survivor的50%,此年龄值与MaxTenuringThreshold两者中更小的值将会成为新的晋升阈值。

Last modification:June 30th, 2021 at 08:11 am
大家一起分享知识,分享快乐