JVM 垃圾回收器
以下是对主流 JVM 垃圾回收器的详细解析,涵盖
一、Serial GC(单线程串行回收器)
二、Parallel GC(吞吐量优先回收器)
三、CMS(Concurrent Mark Sweep,低延迟回收器)
四、G1(Garbage-First,区域分代回收器)
五、ZGC(超低延迟回收器)
六、Shenandoah(OpenJDK 低延迟回收器)
的核心机制、适用场景及横向对比,帮助理解不同垃圾回收器的设计哲学与性能特点。
一、Serial GC(单线程串行回收器)
核心机制
- 算法:
- 新生代:复制算法(Copying Algorithm),将存活对象从 Eden/Survivor 复制到另一块 Survivor。
- 老年代:标记 - 整理算法(Mark-Compact),标记存活对象后压缩内存空间。
- 执行特点:
- 单线程:GC 时需 Stop The World(STW),暂停所有应用线程,全程由一条 GC 线程完成。
- 简单高效:无线程切换开销,适合内存较小的环境。
关键参数
-XX:+UseSerialGC # 启用 Serial GC(默认用于 Client 模式)
-XX:SurvivorRatio=8 # Eden:Survivor 比例(默认 8:1)
适用场景
- 嵌入式设备或 单核心服务器(如小型桌面应用)。
- 内存 < 1GB 的场景,STW 时间可控。
优缺点
优点 | 缺点 |
---|---|
简单可靠,无额外内存开销 | 单线程导致 STW 时间较长 |
适合内存小的场景 | 无法利用多核 CPU 优势 |
二、Parallel GC(吞吐量优先回收器)
核心机制
- 算法:
- 新生代:多线程复制算法,并行执行垃圾回收。
- 老年代:多线程标记 - 整理算法(JDK 8 前为 Serial Old,JDK 9+ 为 Parallel Old)。
- 执行特点:
- 多线程并行:通过
-XX:ParallelGCThreads
设置线程数,利用多核 CPU 缩短 STW 时间。 - 目标吞吐量:通过
-XX:GCTimeRatio
控制 GC 时间占比(默认 99%,即 GC 时间 ≤ 1%)。
- 多线程并行:通过
关键参数
-XX:+UseParallelGC # 启用 Parallel GC(默认用于 Server 模式)
-XX:MaxGCPauseMillis=100 # 目标最大停顿时间(毫秒,动态调整堆大小)
-XX:GCTimeRatio=99 # 吞吐量目标(1/(1+N),N=99 即吞吐量 99%)
适用场景
- 后台计算任务(如大数据处理、科学计算),优先保证吞吐量。
- 堆内存中等大小(如 4-8GB),允许较短 STW 但需高持续处理能力。
优缺点
优点 | 缺点 |
---|---|
多线程提升吞吐量 | 停顿时间仍随堆增大而增长 |
自动调优(自适应策略) | 无法满足低延迟需求 |
三、CMS(Concurrent Mark Sweep,低延迟回收器)
核心机制
- 算法:
- 标记 - 清除(Mark-Sweep):老年代使用该算法,避免整理内存的 STW 开销。
- 分代设计:新生代用 Parallel Scavenge(多线程复制),老年代用 CMS 并发回收。
- 执行阶段(老年代):
- 初始标记(STW):标记根对象,耗时短。
- 并发标记:与应用线程并行标记可达对象。
- 重新标记(STW):修正并发标记期间的变动,耗时短。
- 并发清除:与应用线程并行清除垃圾对象。
关键参数
-XX:+UseConcMarkSweepGC # 启用 CMS GC
-XX:CMSInitiatingOccupancyFraction=70 # 老年代占用 70% 时触发 GC
-XX:+CMSParallelRemarkEnabled # 启用并行重新标记(减少 STW 时间)
适用场景
- 交互式应用(如 Web 服务器、前端服务),需降低 STW 对用户体验的影响。
- 堆内存较大(如 8-16GB),但对象存活率较高(老年代占比大)。
优缺点
优点 | 缺点 |
---|---|
老年代并发回收,STW 时间短 | 标记 - 清除导致内存碎片 |
适合低延迟场景 | 并发阶段占用 CPU 资源 |
分代设计提升回收效率 | 可能触发 "Concurrent Mode Failure"(回收速度慢于分配速度) |
四、G1(Garbage-First,区域分代回收器)
核心机制
- 分区(Region)设计:
- 将堆划分为大小相等的 Region(如 2MB-32MB),每个 Region 可动态扮演 Eden、Survivor、Old 或 Humongous(大对象)。
- 优先回收价值高的区域:通过记录每个 Region 的垃圾占比,优先处理回收收益最大的区域(Garbage-First 得名)。
- 算法:
- 新生代:多线程复制算法,回收 Eden/Survivor 区域。
- 老年代:并发标记 + 混合回收(部分 Old Region + 新生代),基于标记 - 整理算法。
执行阶段
- 初始标记(STW):标记根对象。
- 并发标记:与应用线程并行标记可达对象。
- 最终标记(STW):处理 SATB 日志(Snapshot At The Beginning,记录并发阶段新增引用)。
- 筛选回收(STW):计算各 Region 回收收益,选择部分 Old Region 与新生代混合回收。
关键参数
-XX:+UseG1GC # 启用 G1 GC
-XX:G1HeapRegionSize=4m # 设置 Region 大小(自动推算默认值)
-XX:MaxGCPauseMillis=200 # 目标最大停顿时间(默认 200ms)
-XX:G1MixedGCCountTarget=8 # 混合回收时最大 Region 数
适用场景
- 大内存(8GB+) 且 需要兼顾吞吐量与低延迟 的场景(如微服务、中间件)。
- 对象分配频率高(如新生代大对象多),或存在大量中等大小对象(避免进入 Humongous Region)。
优缺点
优点 | 缺点 |
---|---|
分区设计避免内存碎片 | 内存占用高(每个 Region 需元数据) |
可预测的停顿时间 | 并发标记阶段耗 CPU 资源 |
混合回收应对老年代回收 | 调优复杂度高于 CMS/Parallel |
五、ZGC(超低延迟回收器)
核心机制
- 着色指针(Colored Pointers):
- 将对象引用的低 4 位用于存储 GC 状态(如标记位、重映射状态),无需修改对象头,减少内存访问开销。
- 读屏障(Load Barrier):
- 在读取对象引用时动态修正指针(如对象被移动到新 Region,通过读屏障获取新地址)。
- 并发标记 - 整理:
- 全程几乎无 STW(仅初始标记和再标记有极短停顿,通常 <1ms),支持 TB 级堆内存。
执行阶段
- 初始标记(STW):标记根对象,耗时极短。
- 并发标记:与应用线程并行标记可达对象。
- 再标记(STW):处理并发标记期间的引用变动,耗时极短。
- 并发转移:移动存活对象到新 Region,通过读屏障修正所有引用。
关键参数
-XX:+UseZGC # 启用 ZGC(JDK 11+)
-XX:ZHeapMaxSize=8t # 最大堆内存(支持 TB 级)
-XX:ZCollectionInterval=1000 # 强制 GC 间隔(毫秒,避免碎片累积)
适用场景
- 超大堆内存(16GB-8TB)且 对延迟敏感 的场景(如金融交易、实时数据处理)。
- 云原生环境(如 Kubernetes 弹性扩缩容),需快速启动和低停顿。
优缺点
优点 | 缺点 |
---|---|
停顿时间 <10ms,几乎无感知 | 吞吐量略低于 G1(约 95%) |
支持动态堆大小调整 | 仅 JDK 11+ 可用,需谨慎适配 |
分代设计(JDK 15+)提升年轻代回收效率 |
六、Shenandoah(OpenJDK 低延迟回收器)
核心机制
- 转发指针(Forwarding Pointer):
- 在对象头中添加指针,指向对象的新地址(移动后通过该指针修正引用)。
- 布鲁姆过滤器(Bloom Filter):
- 快速判断对象是否已被移动,减少无效的指针扫描,提升并发性能。
- 并发标记 - 复制:
- 与 ZGC 类似,全程并发执行,仅初始标记和最终标记有短暂 STW。
执行阶段
- 初始标记(STW):标记根对象。
- 并发标记:与应用线程并行标记可达对象。
- 最终标记(STW):处理漏标的对象,耗时短。
- 并发回收:移动存活对象到新 Region,通过转发指针更新引用。
关键参数
-XX:+UseShenandoahGC # 启用 Shenandoah(OpenJDK 12+)
-XX:ShenandoahGCMode=主动/被动 # 触发模式(主动模式基于内存阈值,被动响应分配压力)
-XX:MaxGCPauseMillis=10 # 目标停顿时间(默认 10ms)
适用场景
- 大内存(8GB-2TB)且 需要 OpenJDK 原生支持 的场景(如开源项目、非商业环境)。
- 对吞吐量要求中等,但需严格控制延迟的应用(如消息中间件、实时分析系统)。
优缺点
优点 | 缺点 |
---|---|
停顿时间与 ZGC 相当 | 吞吐量低于 G1(约 90%) |
内存占用低(转发指针仅占对象头) | 商业 JDK 需授权(OpenJDK 免费) |
社区活跃,适配性强 | 调优参数较多,需深入理解机制 |
七、横向对比表格
维度 | Serial GC | Parallel GC | CMS | G1 | ZGC | Shenandoah |
---|---|---|---|---|---|---|
设计目标 | 简单单线程 | 高吞吐量 | 低延迟(老年代) | 平衡吞吐量与延迟 | 超低延迟(TB 级) | 超低延迟(OpenJDK) |
堆大小推荐 | 小(<1GB) | 中等(4-8GB) | 中大(8-16GB) | 大(8GB+) | 超大(16GB+) | 大(8GB-2TB) |
STW 时间 | 长 | 中等 | 短(老年代并发) | 可控(<500ms) | 极短(<10ms) | 极短(<10ms) |
算法核心 | 复制 + 标记整理 | 并行复制 + 整理 | 并发标记 - 清除 | 分区 + 混合回收 | 着色指针 + 并发整理 | 转发指针 + 并发复制 |
适用场景 | 嵌入式 / 单核心 | 后台计算 | Web 服务 | 微服务 / 中间件 | 金融 / 实时数据 | 开源 / OpenJDK 环境 |
JDK 版本 | 全版本 | 全版本 | JDK 1.4+ | JDK 7+ | JDK 11+ | OpenJDK 12+ |
典型参数 | -XX:+UseSerialGC | -XX:+UseParallelGC | -XX:+UseConcMarkSweepGC | -XX:+UseG1GC | -XX:+UseZGC | -XX:+UseShenandoahGC |
八、选择建议
- 小内存 / 简单场景:
- 优先选 Serial GC(单核心)或 Parallel GC(多核、需吞吐量)。
- 中等内存 / 低延迟需求:
- 选 CMS(老年代对象多)或 G1(对象分配频繁、需分代回收)。
- 大内存 / 超低延迟:
- 商业场景选 ZGC(JDK 11+,Oracle/OpenJDK);
- 开源场景选 Shenandoah(OpenJDK 12+,避免授权问题)。
- 云原生 / 弹性扩缩容:
- 优先 ZGC(支持动态堆调整和超大内存)。
九、发展趋势
- ZGC/Shenandoah 主导未来:逐步替代 G1 成为大内存场景的默认选择。
- 分代与并发结合:如 ZGC 支持分代(JDK 15+),提升年轻代回收效率。
- 硬件协同优化:利用 CPU 特性(如 AMD 的 MMU 分页)加速 GC 指针操作。
通过理解不同垃圾回收器的设计 trade-off,可根据具体业务需求(吞吐量、延迟、内存大小)选择最优方案,或通过组合参数(如 G1 的 -XX:InitiatingHeapOccupancyPercent
)进一步调优。