美文网首页邦仁聊技术程序员
一次诡异的Old GC问题排查(跨代引用)

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

作者: 涤生YQ | 来源:发表于2017-06-29 20:13 被阅读701次

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

    问题介绍

    有个业务部门的同事说有个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这些都存在动态扩容,可能会出现跨代引用的问题。

    欢迎关注微信公共号

    相关文章

      网友评论

      • bighome:学习了,以后在用List、Map、Set的时候会注意这方面的问题。:+1:
      • 吴世浩:你这个的意思是这么map已经到老年代,但是引用确还在young代?可是自己设置为null了,怎么还跨代引用?
        涤生YQ:@吴世浩 map 是设置为null了,但是map 实例并没有回收,而且动态扩容过程中的entry数组实例也没有回收,所以young区还是有引用指向old区的entry实例,所以需要young gc先回收掉young区的对象引用。
      • 雪驹:跨代引用是什么意思?
        涤生YQ:@雪驹 比如说对象实例在old区,引用在young区,这种就是跨代引用
      • 阿飞的博客:我认为这种情况,应该要修改代码(new hashMap(cap),防止rehash,这才是最根本的解决办法!
        涤生YQ:@阿飞滴简书 不是任何场景都事先知道大小
      • _淡入淡出:你好,问一下gc监控图是什么组件提供的?
        涤生YQ:@_淡入淡出 cat

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

      本文链接:https://www.haomeiwen.com/subject/ilevcxtx.html