Skip to content

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 版本、收集器和负载模型不同,参数效果可能相反。

知识关系

从当前问题继续深入,或者回到提出这个问题的知识入口。