在高吞吐和低延迟并重的 Java 应用中,垃圾回收(GC)暂停时间直接影响用户体验与系统稳定性。
要将单次停顿从约 200ms ,缩短到约 20ms。
需要系统性地分析与多层面优化,包括配置、内存布局、应用代码和监控反馈闭环。
一、定位与度量
首先,必须准确定位停顿根源。
启用详细 GC 日志(如 -Xlog:gc* 或 JDK8 的 -XX:+PrintGCDetails 等)。
或者,使用实时监控工具(如 JFR、Prometheus + JMX、GCViewer)记录暂停分布、各代回收频率与耗时。

以事实为驱动确定是 Full GC、Young GC 还是混合事件导致 200ms 停顿。
二、选择与调优垃圾回收器
根据延迟目标和负载特性选择合适 GC:
对于对低停顿敏感的应用,考虑使用 ZGC 或 Shenandoah(JDK 11+、JDK17+ 支持),其并发回收和可预测停顿优势明显。

如果使用 G1 GC,需调优堆分区(regions)、暂停目标(-XX:MaxGCPauseMillis)。
和混合回收触发(-XX:+UseG1GC,-XX:InitiatingHeapOccupancyPercent)。
对传统 CMS/G1,可通过增加并发线程(-XX:ConcGCThreads)、调整年轻代大小与 Survivor 比例来减少短停顿。
三、堆与代设置
合理设置堆大小与年轻代比例以减少晋升和频繁的 Full GC:
增加堆可以降低 Full GC 频率,但可能延长单次标记/清理时间;需与并发 GC 结合。
适配年轻代(-Xmn 或 G1 的 region 配置)以避免过多对象过早晋升至老年代。
调整 Survivor 区与对象晋升阈值(-XX:MaxTenuringThreshold)以减少不必要的晋升。
四、减少可回收对象与优化分配模式
应用层面优化往往是最有效手段:
降低短寿命对象分配(对象池、复用可变对象、使用原始类型数组或 ThreadLocal 缓存)。

避免大对象频繁创建,考虑复用或分块管理。
优化集合使用(预估容量避免频繁扩容)、避免不必要的临时字符串拼接与装箱操作。
五、内存碎片与大对象处理
对于大对象(例如大型数组、缓冲区):
使用堆外内存(DirectBuffer)或内存映射文件,减少堆压力。
在 G1 或并发收集器中配置大对象区域或调节阈值,避免触发长时停顿的老年代压缩 / 整理。
六、线程与并发资源
GC 并发线程数应与 CPU 资源匹配:
通过 -XX:ParallelGCThreads、-XX:ConcGCThreads 调整,避免因 GC 抢占导致的应用抖动。
保证应用线程与 GC 线程之间的 CPU 平衡,必要时采用 CPU 亲和性或独占机器部署。