GC 日志里每八分钟出现一次 promotion failed,随后跟一次 Full GC,日志写着 to-space exhausted,接口在那一秒全部超时。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
java -Xms8g -Xmx8g -Xmn3g -XX:SurvivorRatio=8 \ -XX:+UseG1GC -Xlog:gc*:file=/tmp/gc.log:time -jar app.jar grep -c 'promotion failed' /tmp/gc.log
年轻代只有 1G,Survivor 放不下一次 Minor GC 后存活的对象,晋升时老年代也没有连续空间,回收器只能退化成 Full GC。对象创建速率高时更明显。这一点先确认清楚,后面就不用反复试。
碰到「晋升失败」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。
改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。
具体做法是,把年轻代调大 用 -Xmn 或 NewRatio 把 Eden 提到 3G 以上;让 Survivor 能容纳一批存活对象 避免直接晋升;查是否有大对象直接进老年代 调高 PretenureSizeThreshold;用 GC 日志确认 promotion failed 是否消失。
改完照着核一遍,压测一小时 gc.log 里 promotion failed 次数为 0 Full GC 不再被触发。
