我们都知道,survivor区中的对象年龄超过MaxTenuringThreshold后会晋升到老年代,但实际上,只要survivor区中某个年龄段的对象超过一定比例后,大于这个年龄段的对象就会晋升到老年区。
对此,《深入理解java虚拟机》中原话是这样说的:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
我在第一次看到这个相同年龄时,就感觉不太对劲。假设下面这种情况:
年龄 | 比例 |
---|---|
1 | 30% |
2 | 30% |
3 | 30% |
如果计算的是相同年龄所有对象的大小,那么任何一个年龄段的对象都没有超过50的占比,也就不会晋升到老年代,但实际上survivor已经使用了90%,这显然是不合理的。
翻看hotspot源码,hotspot并不是用的相同年龄:
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;
assert(sizes[0] == 0, "no objects with age zero should be recorded");
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;
......
}
首先,在这段代码中,我们可以清晰地看到TargetSurvivorRatio这个变量,他是用来确定survivor区存活比例的。TargetSurvivorRatio默认为50,也就是书中所说的一半,具体大小可以用
-XX:TargetSurvivorRatio=percent命令来设置。
其次,可以看出,总大小total是从年龄为1的对象一直加过来的,直到加上某个年龄对象后total超过设定的比例。最后的result则是返回的真实threshold。
一句话来说,在hotspot中,当survivor中年龄小于等于age的对象占比超过TargetSurvivorRatio时,则年龄大于age的对象会直接晋升到老年代。