GC问题排查实战五-G1日志分析以及动态年龄调整机制
G1分析
这次我们用G1试试。
参数
-Xms8192M -Xmx8192M -Xmn4096M -Xss1M -XX:+UseG1GC -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC -XX:+PrintReferenceGC -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 -XX:MetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/app/log/gc/gc.log -XX:ErrorFile=/data/log/error/error.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/log/heap/heap_hprof.bin -Dfile.encoding=UTF-8
跑完后数据

gc日志数据
同样我们找高的点看看:

看看日志,这个日志比较详细,更加直白的会告诉你哪些步骤耗时,可以看到也是处理引用和拷贝对象,也就是复制的消耗:

然后我找个耗时小的看看:

可以看到这个消耗的比较少,拷贝的对象也不多,引用处理也不多:

safepoint相关数据

动态年龄调整机制(ParNew作为例子)
当GC之后检查幸存区的年龄分布,发现年龄累计(小于等于)已经超过幸存区的一半(-XX:TargetSurvivorRatio默认是50)了,这个时候其实是有个动态年龄计算规则,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值,刚好这个时候年龄2累计(等于小于)的大小刚好超过幸存区的一半了,所以这个时候晋升的年龄就是2,因此后一次GC处理就会把年龄为2的晋升过去了,而且这里可以看到new threshold 2 (max 6),比如最开始是6,年龄为1的没有超过幸存区的一半,所以没调整,没晋升,耗时不多:

第二次GC后发现年龄为2的累计居然超过了幸存区的一半,那就调整阈值,这次耗时不是晋升,是因为处理引用和幸存区复制的耗时,下一次会有晋升了:

第三次就有晋升,比较耗时,主要就是因为复制,以及上一次年龄为1的经过GC后没被清除,年龄变2了,然后阈值又是2,所以他们要晋升,年龄2的又超过幸存区一半了,那下次还有晋升,年龄阈值还是2:

第四次必然有晋升,比较耗时,但是阈值改回来了,以为最后剩下年龄2的没超过幸存区一半,下次处理就不会有晋升了:

后一次就没了晋升了,阈值没变,多了年龄是3的对象,但是累计也不超过幸存区的一半,耗时也少了:

hotspot相关源码jdk1.8-b26
当然这个也动态年龄调整以及晋升不是我自己猜的,看看相关源码,是累计的的值大于幸存区容量乘以TargetSurvivorRatio系数,然后后面有打印的话会打印:

后面打印的就是这堆东西:

哪些收集器会用到这个机制
那我们再看看这个调节是在哪些用到的,首先是默认的新生代收集器DefNewGeneration:

然后是G1CollectorPolicy:

还有一个ParNewGeneration:

这个机制在哪个阶段被使用
我们拿ParNewGeneration的收集过程看看,不过我能力有限,并不打算分析源码,只是把这个动态调节的位置展示下,可以看到这个调节的方法在最后被调用,后面都是打印日志了,也就是说是在收集阶段最后才会进行调整的,然后这个年龄就会影响后一次收集的对象晋升:

年龄阈值在哪个阶段被使用
我们再来看下对象晋升再哪里,再扫描弱引用的时候会调用ParNewGeneration的copy_to_survivor_space方法:


然后就是处理的时候,先判断年龄,小于阈值的就先从to区分配内存,失败的话就说明溢出了,然后就尝试放老年代,还不够的话记录晋升失败,当然幸存区放的下的话就对象年龄**+1**:

其实就是标记后,对活的对象进行年龄阈值判断,处理能不能放下,放下年龄**+1**,或者放不下晋升或者失败。
总结
通过这3种垃圾回收器回收年轻代的例子,想说明如果发现年老代收集的耗时很长,可以通过什么方式来排查,首先当然是要有足够的日志,比如引用处理,对象年龄,对象拷贝,像G1的话就很详细,当然还可能有safepoint的等待线程的问题,然后根据具体的日志内容进行分析,看看到底哪块是主要耗时,然后进行优化,举个例子,比如我不想那么早把这些生命周期很短的晋升到老年代,放置老年代频繁的GC,那我可以调大年轻代以及幸存区比例,让他尽可能在年轻代就被清除,当然还可以调整比较幸存区比例的那个参数,当然也不是一味的调大年轻代,如果刚好业务对象存活的时间久,就会在幸存区频繁的复制,也是耗时的,所以没有万能的策略,只有根据具体业务选择不同的策略,这个就要看对业务以及垃圾回收器的理解啦,当然多学点理论知识还是好的,JVM相对来说理论比较多,虽然貌似实战没啥用,但是知道总比不知道好,手里有剑跟有剑不用是两码事对吧。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限。
本文通过一个GC问题排查实战,聚焦G1垃圾收集器的日志分析,探讨动态年龄调整机制在ParNew和G1中的应用。分析了不同GC周期的耗时、对象晋升与年龄阈值变化,揭示了如何根据日志信息优化年轻代配置,以减少老年代GC的压力。同时,介绍了相关源码以加深理解。
354

被折叠的 条评论
为什么被折叠?



