由GC日志中Desired Survivor Size引起的思考
# ...
139.274: [GC (Allocation Failure) 139.274: [ParNew Desired survivor size 64421888 bytes, new threshold 4 (max 15)
- age 1: 44528360 bytes, 44528360 total
- age 2: 4879968 bytes, 49408328 total
- age 3: 13927288 bytes, 63335616 total
- age 4: 4104864 bytes, 67440480 total
- age 5: 10940112 bytes, 78380592 total
: 1075651K->93432K(1132672K), 0.0638933 secs] 1075651K->93432K(1971328K), 0.0640178 secs] [Times: user=0.38 sys=0.00, real=0.06 secs]
Heap after GC invocation5 (full 0):
par new generation total 1132672K, used 93432K[0x0000000000000000, 0x00000000ccd00000, 0x00000000ccd00000)
eden space 1006848K, 0% used [0x0000000000000000, 0x0000000080000000, 0x00000000bd740000)
from space 125824K, 74% used [0x00000000c5220000, 0x00000000cad5e0a0, 0x00000000ccd00000)
to space 125824K, 0% used [0x00000000bd740000, 0x00000000bd740000, 0x00000000c5220000)
concurrent mark-sweep generation total 838656K, used 0K [0x00000000ccd00000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 81264K, capacity 83996K, committed 84352K, reserved 1122304K
class space used 11020K, capacity 11525K, committed 11648K, reserved 1048576K
# ...
这里主要关注这一段
Desired survivor size 64421888 bytes, new threshold 4 (max 15)
- age 1: 44528360 bytes, 44528360 total
- age 2: 4879968 bytes, 49408328 total
- age 3: 13927288 bytes, 63335616 total
- age 4: 4104864 bytes, 67440480 total
- age 5: 10940112 bytes, 78380592 total
- new threshold 4:新的晋升阈值
- max 15:最大阈值
- age n: 表示GC年龄
- age n后的第一个bytes:当前年龄对象占survivor区域的总内存大小
- age n后的第二个bytes:年龄小于等于当前年龄的对象占survivor区域的总内存大小
然后有一个Desired survivor size
,字面意思为,期望的survivor大小
该大小与JVM参数-XX:TargetSurvivorRatio(设定目标survivor的使用率,默认为50)
相关,该参数用于动态年龄判断
这里复习一下对象如何进入老年代,通常有4种情况会导致对象进入老年代
- 动态年龄判断
- 大对象直接进入老年代
- 超过MaxTenuringThreshold阈值年龄(默认15)的对象进入老年代
- 空间分配担保机制,如Survivor区无法放入所有的存活对象时
动态年龄判断
意思是程序运行的过程中,JVM会动态的计算晋升到老年代的对象的年龄阈值
在《深入理解Java虚拟机(第三版)》一书中写到:为了能更好地适应不同程序的内存状况, HotSpot虚拟机并不是永远要求对象的年龄必须达到-XX: MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半, 年龄大于或等于该年龄的对象就可以直接进入老年代, 无须等到-XX:MaxTenuringThreshold中要求的年龄。
这里其实是有点问题的(也可能是JDK版本不同),查看OpenJDK8u40的源代码,其实现为
通过阅读源代码,可以得知动态年龄判断的规则为:当某一年龄段之下(包括该年龄段)的对象占用survivor区域内存的50%(默认)时,触发晋升,大于等于该年龄的对象晋升到老年代