一次诡异的Old GC问题排查(跨代引用)

简书 涤生
转载请注明原创出处,谢谢!
如果读完觉得有收获的话,欢迎点赞加关注。

问题介绍

有个业务部门的同事说有个GC问题困扰他好几天,他的应用是个job类型的应用,定时执行任务,任务执行过程中以及执行后频繁Old GC,一直不能恢复。

gc监控图.png

大致情形就是图中如所示(图中显示Full GC,是由于监控系统将Old GC显示成Full GC,其实是Old GC,这个可以通过GC日志确认)

原因锁定

看到这个问题,我先上机器jmap -histo pid看了下是什么对象比较大导致一直Old GC,不能恢复。

第一次histo图

如上图,看到是个比较大的HashMap。首先,立即就跟他确认了下,是job任务执行中还是执行结束之后,他根据日志确认执行中和执行结束都有这种情况,而且在每次任务执行结束,都将Map赋值为null,也就是说正常情况任务执行结束,应该会回收此Map。然后,看了下代码,确认确实在任务结束后,没有其他地方引用该对象。

根据上面的现象,当时就怀疑是不是Map动态扩容产生跨代引用(这里所说的跨代引用指的是新生代对象引用了老年代对象)导致的,仅仅Old GC回收不了在Young区具有引用的Map;并且从监控图上看,情况也是一直Old GC,但没有Young GC,所以更加证实了这个猜测。

为了确认问题,执行了下jmap -histo:live pid(这个会触发一次Full GC),然后又jmap -histo pid看了下果然Map对象被回收了。

第二次histo图

而且从GC监控图上看51分后,GC也正常了。

最后看了下jvm参数配置,缺少-XX:+CMSScavengeBeforeRemark这个参数,然后让他加上这个参数,避免下次在发生这种问题,这个参数是用来在做CMS Old GC 时先进行一次Young GC。

结束语

List、Map、Set这些都存在动态扩容,可能会出现跨代引用的问题。


个人微信公共号,感兴趣的关注下,获取更多技术文章

涤生-微信公共号

推荐阅读更多精彩内容