GC问题排查实战五-G1日志分析以及动态年龄调整机制

本文通过一个GC问题排查实战,聚焦G1垃圾收集器的日志分析,探讨动态年龄调整机制在ParNew和G1中的应用。分析了不同GC周期的耗时、对象晋升与年龄阈值变化,揭示了如何根据日志信息优化年轻代配置,以减少老年代GC的压力。同时,介绍了相关源码以加深理解。
摘要由CSDN通过智能技术生成

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的收集过程看看,不过我能力有限,并不打算分析源码,只是把这个动态调节的位置展示下,可以看到这个调节的方法在最后被调用,后面都是打印日志了,也就是说是在收集阶段最后才会进行调整的,然后这个年龄就会影响后一次收集的对象晋升:
在这里插入图片描述

年龄阈值在哪个阶段被使用

我们再来看下对象晋升再哪里,再扫描弱引用的时候会调用ParNewGenerationcopy_to_survivor_space方法:
在这里插入图片描述
在这里插入图片描述

然后就是处理的时候,先判断年龄,小于阈值的就先从to区分配内存,失败的话就说明溢出了,然后就尝试放老年代,还不够的话记录晋升失败,当然幸存区放的下的话就对象年龄**+1**:
在这里插入图片描述
其实就是标记后,对活的对象进行年龄阈值判断,处理能不能放下,放下年龄**+1**,或者放不下晋升或者失败。

总结

通过这3种垃圾回收器回收年轻代的例子,想说明如果发现年老代收集的耗时很长,可以通过什么方式来排查,首先当然是要有足够的日志,比如引用处理,对象年龄,对象拷贝,像G1的话就很详细,当然还可能有safepoint的等待线程的问题,然后根据具体的日志内容进行分析,看看到底哪块是主要耗时,然后进行优化,举个例子,比如我不想那么早把这些生命周期很短的晋升到老年代,放置老年代频繁的GC,那我可以调大年轻代以及幸存区比例,让他尽可能在年轻代就被清除,当然还可以调整比较幸存区比例的那个参数,当然也不是一味的调大年轻代,如果刚好业务对象存活的时间久,就会在幸存区频繁的复制,也是耗时的,所以没有万能的策略,只有根据具体业务选择不同的策略,这个就要看对业务以及垃圾回收器的理解啦,当然多学点理论知识还是好的,JVM相对来说理论比较多,虽然貌似实战没啥用,但是知道总比不知道好,手里有剑跟有剑不用是两码事对吧。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值