记一次新生代晋升担保失败导致的full gc

2022-05-19  本文已影响0人  justonemoretry

现象

线上接口突然出现大量超时,仔细一看,超时的dubbo provider都是一台机器,一看,果然又是full gc耗时过长,最近真和这个干上了,上次是weak reference处理时间过长,这次是新生代晋升担保失败promotion failed。

问题分析

先看gc日志,毕竟这个是最容易拿到的, 对应日志如下:

108817.632: [GC (Allocation Failure) 108817.632: [ParNew (promotion failed): 2357878K->2354600K(2643200K), 0.6570478 secs]
108818.289: [CMS: 2615283K->256960K(2936832K), 4.1329887 secs] 4972611K->256960K(5580032K), [Metaspace: 159337K->159337K(1204224K)], 4.7907632 secs]
 [Times: user=3.54 sys=1.31, real=4.79 secs] 

从日志可以看出,很明显是新生代promotion failed导致了cms进行了带整理的full gc,即mark-sweep-compact,并且这个过程是串行的,会回收新生代、老年代和metaspace元数据空间,可以看到这个过程花了4.79s,导致接口出现大量超时。

promotion failed的原因

promotion failed的原因一般有以下两个:
内存碎片
顾名思义,晋升担保失败就是指在进行 Young GC 时,Survivor 放不下,对象只能放入 Old,但此时 Old 也放不下。直觉上乍一看这种情况可能会经常发生,但其实因为有 concurrentMarkSweepThread 和担保机制的存在,发生的条件是很苛刻的,除非是短时间将 Old 区的剩余空间迅速填满,例如上文中说的动态年龄判断导致的过早晋升(见下文的增量收集担保失败)。另外还有一种情况就是内存碎片导致的 Promotion Failed,Young GC 以为 Old 有足够的空间,结果到分配时,晋级的大对象找不到连续的空间存放。
增量收集担保失败
分配内存失败后,会判断统计得到的 Young GC 晋升到 Old 的平均大小,以及当前 Young 区已使用的大小也就是最大可能晋升的对象大小,是否大于 Old 区的剩余空间。只要 CMS 的剩余空间比前两者的任意一者大,CMS 就认为晋升还是安全的,反之,则代表不安全,不进行Young GC,直接触发Full GC。
具体原因的确定和解决方式

参考链接

Java中9种常见的CMS GC问题分析与解决

上一篇 下一篇

猜你喜欢

热点阅读