java -Xms8g -Xmx8g -Xmn2g -XX:SurvivorRatio=8 \ -Xlog:gc*:file=/tmp/gc.log:time -jar app.jar jstat -gc 21455 1000 30
jstat -gc 显示 YGC 每秒 3 次,单次回收 40MB,YGCT 一分钟累计 1.8 秒,接口 P99 被这些短停顿拖到 400 毫秒。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。
原因不复杂,年轻代只有 256MB,Eden 很快被临时对象填满,回收频率高但每次能收的少。对象分配速率没变,缩小新生代只是把一次停顿拆成很多次。
这次卡住的地方就在「Minor GC 频繁」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。改完隔一天再看一次,当场正常不代表能稳住。
测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。
处理上按这个顺序:把年轻代调到 1G 到 2G 减少回收次数;用 -Xmn 或 -XX:NewRatio 明确大小 不要只靠默认比例;观察每次 Minor GC 的回收量是否同步变大;对比调整前后的 YGC 次数和 P99。
改完只确认一件事:YGC 降到每秒 0.5 次以下 单次停顿仍在 50 毫秒内 P99 回到 200 毫秒。
