一、概述
系统上线会出现GC相关问题,有FGC过于频繁的,有YoungGC耗时过长的。GC过程会导致其他线程STW,进一步导致服务器响应超时。
二、解决
1. 通过可视化工具(JvisualVM或MAT)或者终端命令行查看每次GC之后的情况
2. 如果每次GC之后内存都没有释放空间的话,这就可能是内存泄露的问题。
3. 接下来查看heap dump文件,对堆内存中的对象进行查看,进行大小排序。
4. 观察代码,发现错误。
5. 可以设置扩大CMS收集器的分代年龄,YGC中存活对象转为老年代的对象次数增多。
三、GC什么时候发生?
YGC
大多数情况下,对象直接在年轻代中的Eden区进行分配,如果Eden区域没有足够的空间,那么就会触发YGC(Minor GC),YGC处理的区域只有新生代。因为大部分对象在短时间内都是可收回掉的,因此YGC后只有极少数的对象能存活下来,而被移动到S0区(采用的是复制算法)。当触发下一次YGC时,会将Eden区和S0区的存活对象移动到S1区,同时清空Eden区和S0区。当再次触发YGC时,这时候处理的区域就变成了Eden区和S1区(即S0和S1进行角色交换)。每经过一次YGC,存活对象的年龄就会加1。
FGC
下面四种情况,对象会进入到老年代。当老年代内存不够会触发FGC:
除此之外,以下情况也会触发FGC:
四、什么情况下GC会对程序造成影响
不管YGC还是FGC,都会造成一定程度的程序卡顿(即Stop The World问题:GC线程开始工作,其他工作线程被挂起),即使采用ParNew、CMS或者G1这些更先进的垃圾回收算法,也只是在减少卡顿时间,而并不能完全消除卡顿。
那到底什么情况下,GC会对程序产生影响呢?根据严重程度从高到底,我认为包括以下4种情况:
FGC过于频繁:FGC通常是比较慢的,少则几百毫秒,多则几秒,正常情况FGC每隔几个小时甚至几天才执行一次,对系统的影响还能接受。但是,一旦出现FGC频繁(比如几十分钟就会执行一次),这种肯定是存在问题的,它会导致工作线程频繁被停止,让系统看起来一直有卡顿现象,也会使得程序的整体性能变差。
其中,「FGC过于频繁」和「YGC耗时过长」,这两种情况属于比较典型的GC问题,大概率会对程序的服务质量产生影响。剩余两种情况的严重程度低一些,但是对于高并发或者高可用的程序也需要关注。
五、实践指南
通过上面的案例分析以及理论介绍,再总结下FGC问题的排查思路,作为一份实践指南供大家参考。
1. 清楚从程序角度,有哪些原因导致FGC?
2. 清楚排查问题时能使用哪些工具
# 查看堆内存各区域的使用率以及GC情况 jstat -gcutil -h20 pid 1000 # 查看堆内存中的存活对象,并按空间排序 jmap -histo pid | head -n20 # dump堆内存文件 jmap -dump:format=b,file=heap pid
3. 排查指南
原文:https://www.cnblogs.com/qmillet/p/12970595.html