简书 涤生。
转载请注明原创出处,谢谢!
如果读完觉得有收获的话,欢迎点赞加关注。
问题介绍
有个业务部门的同事说有个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这些都存在动态扩容,可能会出现跨代引用的问题。
欢迎关注微信公共号
网友评论