关于-XX:MaxTenuringThreshold,我想大家肯定都不陌生。
-XX:MaxTenuringThreshold:Maximum value for tenuring threshold.即年轻代晋升老年代的最大年龄阈值。
关于这个参数,我想大部分人都知道的是默认值是15,而熟悉JVM的同学知道:
- 为什么最大是15.
- 在CMS下是6.
- 该参数只是初始值并会动态改变.
我以为这就是此参数的所有相关知识点了,没想到其实还有一些细节被我忽略了,<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来查看)


-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发现:
我去!还真是15,再看打印信息:
惊了,这是什么情况,和我的认知产生了冲突呀。
肿么办,赶紧去翻翻源码吧
翻了半天,我在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
果然
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两者中更小的值将会成为新的晋升阈值。
One comment
最后的结论写错了