由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的源代码,其实现为

image.png

通过阅读源代码,可以得知动态年龄判断的规则为:当某一年龄段之下(包括该年龄段)的对象占用survivor区域内存的50%(默认)时,触发晋升,大于等于该年龄的对象晋升到老年代