主题
Java 服务的 GC 延迟、吞吐和内存占用应该怎样调优?
题型:调优 · 先建立基线,再区分分配问题、存活问题和收集器问题。
建议回答
我会先明确服务目标:关注吞吐优先、尾延迟优先,还是容器内存成本优先,然后在稳定流量下记录业务 P95/P99、GC 停顿占比、分配速率、晋升速率、回收后老年代占用、并发周期耗时和进程 RSS。GC 日志、JFR、JMX 指标和容器内存指标要结合看,不能只盯单次停顿。
优化顺序通常是先修应用分配:减少无意义的临时对象、超大集合和重复序列化,控制批量大小与缓存上限;再确认堆是否过小、过大或缺少并发回收余量;最后才调整收集器参数。使用 G1 时会关注并发标记启动是否过晚、Mixed GC 是否回收不足、Humongous 分配和疏散失败,而不是一次改很多参数。对极低延迟目标,可以评估 ZGC 等并发收集器,但要用真实工作负载验证 CPU、内存和吞吐代价。
每次只改变少量变量,经过压测、灰度和长稳测试确认。验收不只看平均停顿,还要确认业务尾延迟、吞吐、错误率、CPU、RSS 以及回收后的内存基线没有恶化。
关键指标
- 业务端到端 P95/P99 与 GC 停顿时间、频率、总占比。
- 对象分配速率、晋升速率、老年代回收后占用和堆增长斜率。
- CPU、吞吐、进程 RSS、容器 OOM 次数和每实例承载量。
- 并发标记周期、Mixed GC 效率、Humongous Region 和疏散失败次数。
关键权衡
- 增大堆可能减少回收频率,但会增加内存成本、预热时间和某些回收阶段的工作量。
- 更短停顿目标可能让 GC 更频繁并占用更多 CPU,业务吞吐未必更高。
- 更换收集器会改变延迟、吞吐和资源曲线,也会增加参数与运维验证成本。
易错点
- 不要把“加大
-Xmx”当成内存泄漏或高分配速率的永久解决方案。 - 不要脱离业务延迟只比较 GC 日志,也不要用一次短压测得出结论。
- 不要机械复制网上参数;JDK 版本、收集器和负载模型不同,参数效果可能相反。