jvm垃圾回收优化核心是吞吐、延迟、内存占用三者平衡;web api选zgc/g1保低延迟,批处理用parallel gc提吞吐,容器化推荐maxrampercentage动态调堆。

JVM 垃圾回收策略的性能优化,核心在于匹配业务特征、控制停顿、减少无效回收。不是堆越大越好,也不是 GC 越快越优,关键是在吞吐量、延迟、内存占用三者之间找到合理平衡点。
明确调优目标,再选回收器
不同场景对 GC 的诉求差异很大: - Web API 或实时服务:优先低延迟,目标是单次 GC 停顿 ≤ 200ms,可选 G1(JDK8+ 默认)、ZGC(JDK11+)或 Shenandoah; - 批处理、离线计算任务:追求高吞吐,允许较长但可控的停顿,Parallel GC 更合适; - 容器化部署(如 Kubernetes):需避免堆内存超限被 OOMKilled,建议用 `-XX:MaxRAMPercentage=70` 替代固定 `-Xmx`; - 小内存环境(堆结构与分代参数要协同调整 新生代太小 → Minor GC 频繁,CPU 被大量消耗;太大 → 晋升过快,老年代压力上升,易触发 Full GC。 建议按以下逻辑配置: - 固定初始堆与最大堆:`-Xms4g -Xmx4g`,避免运行时动态扩容缩容带来的抖动; - 新生代占比合理:G1 下用 `-XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=60`,Parallel 下可通过 `-XX:NewRatio=2`(即新生代占 1/3)控制; - 控制晋升阈值:`-XX:MaxTenuringThreshold=6`(默认 15),避免短生命周期对象“误入”老年代; - 触发并发标记时机:G1 中 `-XX:InitiatingHeapOccupancyPercent=40`(低于默认 45%)可提前介入,防老年代突增。减少 GC 压力,从代码和对象生命周期入手
GC 调参只是后手,真正高效的做法是降低垃圾生成率: - 避免在循环内创建临时对象(如 `new SimpleDateFormat()`),改用 `ThreadLocal` 或预实例化; - 合理使用对象池(如 Apache Commons Pool),但仅适用于构造/销毁开销大的对象(如 Socket、DB 连接),非万能解药; - 及时置空强引用:方法内局部集合不再使用时设为 `null`,尤其在长生命周期对象中持有大集合时; - 用基本类型替代包装类:`int` 优于 `Integer`,减少自动装箱带来的短期对象; - 谨慎使用 `finalizer` 和 `Cleaner`,它们会延长对象生命周期,且执行不可控。监控必须前置,调优才有依据
没有监控的调优等于盲调。上线前至少做到: - 开启详细 GC 日志:`-Xlog:gc*,gc+heap=debug:file=gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps`(JDK10+); - 实时观察:`jstat -gc不复杂但容易忽略。











