本文目录导读:

- 极致低延迟:ZGC 与 Shenandoah
- 低延迟但吞吐量高:G1(默认GC,JDK 9+)
- 中等延迟:Parallel Scavenge + Parallel Old(吞吐量优先的默认GC)
- 高延迟且不可预测:Serial / Serial Old 与 CMS(已废弃但仍有历史影响)
- 核心对比总结表
- 如何选择以降低延迟?
- 最终建议
不同垃圾回收器(GC)对系统延迟的影响主要体现在 “停顿时间”(Stop-The-World, STW) 的长短和频率上,延迟敏感型应用(如在线交易、实时游戏、流媒体)需要低停顿,而吞吐量优先型应用(如离线批处理)则更关注总体处理能力。
以下是主流垃圾回收器对系统延迟的具体影响分析,按延迟敏感性从高到低排列:
极致低延迟:ZGC 与 Shenandoah
- 核心机制: 基于“并发”和“染色指针”或“读屏障”技术,几乎所有耗时的阶段(标记、清理、甚至压缩)都与用户线程同时运行。
- 对延迟的影响:
- 停顿时间极短: 通常在 1毫秒以下,且与堆大小无关(理论上几十GB的堆停顿也在10ms内)。
- 频率: 停顿是非常短暂的(仅用于初始标记、最终标记等同步点)。
- 特点: 适合对响应时间要求极其苛刻的系统,如证券交易所、高频交易、大型在线游戏服务器、低延迟微服务。
- 代价: 会稍微降低吞吐量(CPU需要额外处理屏障),且内存占用略高。
低延迟但吞吐量高:G1(默认GC,JDK 9+)
- 核心机制: 将堆划分为众多独立的“Region”,优先回收包含最多垃圾的Region(Garbage First),设计目标是可预测的停顿时间。
- 对延迟的影响:
- 停顿时间可控: 通过
-XX:MaxGCPauseMillis(默认200ms) 参数,它能尽力将STW时间控制在这个目标内(但不一定总能严格保证)。 - 频率: 停顿频率取决于Region的回收速度,通常几十到几百毫秒一次。
- 特点: 平衡了延迟和吞吐量,大多数响应时间在100-500ms的Web应用、后台服务都能良好运行。
- 极限情况: 当对象分配速度极快或堆内存不足时,G1会退化到“Full GC”(单线程串行回收),导致延迟飙升到秒级甚至分钟级。
- 停顿时间可控: 通过
中等延迟:Parallel Scavenge + Parallel Old(吞吐量优先的默认GC)
- 核心机制: 关注吞吐量(CPU用于业务处理的时间占比),而不是单次停顿时间。
- 对延迟的影响:
- 停顿时间长: 在CMS(已废弃)或Parallel中,Major GC(老年代GC)和Full GC会进行完全STW。
- 停顿时间: 通常从几百毫秒到几秒不等,如果堆很大(如几十GB),一次Full GC停顿可能达到几十秒。
- 特点: 不适合延迟敏感应用,但如果你运行的是不关注响应时间、只关注CPU利用率的离线任务(如Hadoop/Spark计算、批量导出),它是最高效的。
- 对延迟的影响:
高延迟且不可预测:Serial / Serial Old 与 CMS(已废弃但仍有历史影响)
- Serial: 单线程、STW,停顿时间与堆大小成正比。延迟极高,通常用于桌面应用或小内存嵌入式系统。
- CMS(Concurrent Mark Sweep,JDK 14正式移除):
- 伪低延迟: 其并发标记阶段确实不STW,但在重新标记和并发清理阶段会出现短暂的STW。
- 致命问题: 无法处理“浮动垃圾”,会导致Concurrent Mode Failure,进而触发Serial Old Full GC,停顿时间可能长达几十秒甚至几分钟,延迟极其不稳定。
- CMS是第一个尝试低延迟的GC,但实际表现比G1差很多,已被淘汰。
核心对比总结表
| 垃圾回收器 | 文件 | 主要优化目标 | 典型停顿时间 (STW) | GC频率 | 对延迟影响 | 适用场景 |
|---|---|---|---|---|---|---|
| ZGC / Shenandoah | JDK 11+ / 8+ | 延迟 | < 1ms(几乎无感) | 高频但极短 | 极低 | 超低延迟交易、实时系统、大型在线服务 |
| G1 | JDK 7+ (JDK9默认) | 延迟与吞吐量平衡 | 10ms - 200ms(可控) | 中等 | 低 | 大多数Web服务、企业应用、中大型内存系统 |
| Parallel | JDK 8默认 | 吞吐量 | 100ms - 数秒 | 低频但长 | 高 | 离线批处理、数据挖掘、科学计算 |
| Serial / CMS | 旧/维护 | 单核/首次尝试低延迟 | 数秒 - 数分钟 | 不可预测 | 极高且不稳定 | 小内存桌面、早期系统维护 |
如何选择以降低延迟?
- 如果你的应用要求99%的请求响应时间在10ms内,必须使用 ZGC 或 Shenandoah(JDK 17+ 更成熟)。
- 如果你的应用要求200ms内完成大部分GC,且堆大小小于64GB,可以使用 G1 并调整
-XX:MaxGCPauseMillis为 100-200。 - 如果你的应用是后台批处理,请使用 Parallel,它将全力以赴完成工作,不需要关心用户的等待。
- 避免使用
-XX:+UseConcMarkSweepGC(CMS),它已被JDK 9+标记为弃用并移除。
最终建议
- 对于新项目(JDK 17+): 如果你的系统延迟敏感,直接选择 ZGC(
-XX:+UseZGC),如果延迟要求稍低,用 G1(默认)。 - 如果遇到长停顿: 优先检查堆是否过大、分配速率是否过高、Full GC触发原因(如Promotion Failed),仅靠换GC有时不能解决根本问题,可能还需要调整内存大小或代码对象创建模式(如减少大对象、使用对象池)。
- 最有力的监控: 使用
-Xlog:gc*和工具(如GCViewer、JDK Mission Control)分析停顿时间,而不是仅凭印象猜测。