前提概要
什么是GC问题
堆内内存泄漏总是和GC异常相伴。不过GC问题不只是和内存问题相关,还有可能引起CPU负载、网络问题等系列并发症,只是相对来说和内存联系紧密些,所以我们在此单独总结一下GC相关问题。
针对GC日志,我们就能大致推断出youngGC与fullGC是否过于频繁或者耗时过长,从而对症下药。我们下面将对G1垃圾收集器来做分析,这边也建议大家使用G1-XX:+UseG1GC
。
youngGC过频繁
youngGC
频繁一般是短周期小对象较多
先考虑是不是Eden区/新生代设置的太小了。
看能否通过调整
-Xmn、-XX:SurvivorRatio
等参数设置来解决问题。
youngGC耗时过长
耗时过长问题就要看GC日志里耗时耗在哪一块了。
以G1日志为例,可以关注Root Scanning、Object Copy、Ref Proc等阶段。
Ref Proc耗时长,就要注意引用相关的对象。
Root Scanning耗时长,就要注意线程数、跨代引用。
Object Copy则需要关注对象生存周期。
而且耗时分析它需要横向比较,就是和其他项目或者正常时间段的耗时比较。比如说图中的Root Scanning和正常时间段比增长较多,那就是起的线程太多了。
触发fullGC
G1中更多的还是mixedGC,但mixedGC可以和youngGC思路一样去排查。触发fullGC了一般都会有问题,G1会退化使用Serial收集器来完成垃圾的清理工作,暂停时长达到秒级别,可以说是半跪了。
fullGC的原因可能包括以下这些,以及参数调整方面的一些思路:
晋升失败:在GC的时候没有足够的内存供存活/晋升对象使用,所以触发了FullGC。
这时候可以通过
-XX:G1ReservePercent
来增加预留内存百分比,减少-XX:InitiatingHeapOccupancyPercent
来提前启动标记,-XX:ConcGCThreads来增加标记线程数也是可以的。大对象分配失败:大对象找不到合适的region空间进行分配,就会进行fullGC,这种情况下可以增大内存或者增大
-XX:G1HeapRegionSize
。
程序主动执行System.gc:不要随便写就对了 另外,我们可以在启动参数中配置-XX:HeapDumpPath=/xxx/dump.hprof
来dump fullGC相关的文件,并通过jinfo来进行gc前后的dump
jinfo -flag +HeapDumpBeforeFullGC pid
jinfo -flag +HeapDumpAfterFullGC pid