【背景】
10 年性能优化经验,负责一个 Java 17 高并发订单服务(K8s 1.28,4C8G Pod,G1 GC,QPS 峰值 5000)。服务 P50 稳定 15ms,P99 SLO < 100ms。
【现象】
- 设了 -XX:MaxGCPauseMillis=200,但线上 P99 经常飙到 300-500ms
- GC 日志显示:Young GC 平均 40-60ms,正常;但每天 1-3 次 Mixed GC 退化为 Full GC,耗时 300-800ms
- P99 毛刺和 Full GC 时间高度对齐(GC 日志时间戳 ±50ms 内)
- Full GC 后堆使用率从 ~85% 降到 ~30%,说明有大量长生命周期对象(订单历史数据、本地缓存)
【已尝试的排查与结果】
- 堆从 4G → 8G:Full GC 频率降低(3次/天 → 1次/天),但单次耗时没变(300-800ms),P99 还是会被拖高
- -XX:G1HeapRegionSize=16m(默认 4m):无明显改善
- -XX:InitiatingHeapOccupancyPercent=35(默认 45,提前触发并发回收):Full GC 从 1次/天 → 0.5次/天,但单次耗时不变
- -XX:G1MixedGCCountTarget=4(默认 8,增加每次 Mixed GC 回收入量):无明显改善
- 换 ZGC:P99 稳定在 80ms 以内,但 QPS 从 5000 → 4250(-15%),业务方不接受
- 加了 GC 日志告警(JMX + Prometheus),但只能「事后」看到 Full GC,不能「提前」预警
【卡点】
- MaxGCPauseMillis 到底是「硬约束」还是「软目标」?G1 在什么具体条件下会放弃这个目标?我理解是 Mixed GC 无法在目标时间内完成就会退化为 Full GC,但具体的「放弃条件」(比如 old 区碎片率阈值、pending card table 数量、humongous 对象占比)是什么?有没有源码级别的解释?
- 有没有一个指标(比如 eden 区使用速率、old 区晋升速率、humongous 对象数量)能提前 30 秒预警「下一次 GC 会超时」?我想做的是:在 Full GC 发生前 30 秒触发降级(暂停非核心功能、降低 QPS),而不是事后复盘。
- 如果必须用 G1(ZGC 吞吐损失不可接受),有没有一个具体的调参组合(不是「试试调大堆」这种泛泛建议)能显著减少 Full GC 退化?最好能说明每个参数的原理和适用场景。
【期望得到的答案形态】
- 一个具体的调参组合 + 每个参数的原理(为什么这么设、适用什么场景)
- 一个可落地的监控指标 + 阈值 + 告警规则(Prometheus 表达式或等价物)
- 最好有真实案例:「我们也是这样,后来发现是 XXX,改了 YYY 就好了」