# 垃圾收集器详解
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
如果说垃圾收集算法是内存回收的“方法论”,那么**垃圾收集器(GC Collector)**就是它的“具体实现”。

# 一、主流垃圾收集器总览
JVM 内置了多种收集器,它们各自适用于不同的应用场景与性能目标。 总体可分为三类:
| 分类 | 特点 | 代表收集器 | 目标 |
|---|---|---|---|
| 串行(Serial) | 单线程,Stop-The-World,简单高效 | Serial、Serial Old | 小堆 / 单核环境 |
| 并行(Parallel) | 多线程,关注吞吐量 | Parallel Scavenge、Parallel Old | 吞吐量优先 |
| 并发(Concurrent) | 与应用线程并发执行,低停顿 | CMS、G1 | 响应时间优先 |
# 二、各收集器详解
# Serial 收集器(新生代)
单线程、Stop-The-World 收集器,适用于小堆和单核环境。 GC 期间仅一个线程负责回收,所有用户线程暂停。
算法:复制算法(Copying) 将存活对象从 Eden、Survivor 区复制到另一块空闲 Survivor 区,避免碎片化。
执行流程:
- 新生代内存达到阈值触发 Minor GC;
- 暂停所有用户线程(STW);
- 单线程复制存活对象;
- 回收完成,恢复线程执行。
简单稳定,小内存下性能出色;但在多核或大堆场景下停顿明显。
# Serial Old 收集器(老年代)
Serial 的老年代版本,同样是单线程 STW。
算法:标记-整理(Mark-Compact) 先标记存活对象,再整理内存,保证空间连续性。
执行流程:
- 触发 Full GC → STW;
- 标记老年代存活对象;
- 整理、压缩内存;
- 恢复应用线程。
常用于单线程或 CMS 失败后的回退(Fallback)。
# ParNew 收集器(新生代)
Serial 的多线程版本,在多核 CPU 环境下提升 GC 效率。 属于 STW 并行收集器。
算法:复制算法(Copying) 同 Serial,使用多线程并行复制对象。
执行流程:
- 触发 Minor GC → STW;
- 多线程并行复制存活对象到另一 Survivor 区;
- 完成后恢复应用线程。
JDK 8 以前常与 CMS 搭配使用:ParNew + CMS。
# Parallel Scavenge 收集器(新生代)
与 ParNew 类似,但目标不同: 关注吞吐量(Throughput),即最大化程序运行时间、最小化 GC 时间。
算法:复制算法(Copying)
特性:
- 自动调整堆比例与 GC 时间;
- 通过参数
-XX:MaxGCPauseMillis、-XX:GCTimeRatio控制平衡点; - 适合批处理、后台计算等吞吐量优先场景。
流程:
- Minor GC → STW;
- 多线程复制存活对象;
- 根据吞吐量目标自动调整参数;
- 恢复应用线程。
# Parallel Old 收集器(老年代)
Parallel Scavenge 的老年代版本,同样注重高吞吐量。
算法:标记-整理(Mark-Compact) 通过多线程并行标记和整理对象,避免碎片。
执行流程:
- Full GC → STW;
- 多线程并行标记存活对象;
- 多线程整理、压缩;
- 恢复线程。
Parallel Scavenge + Parallel Old 是吞吐量优先型组合,常用于计算密集型系统。
# CMS 收集器
JDK 14 起被移除,JDK 9 开始默认被 G1 替代。
CMS 是一款基于 标记-清除算法(Mark-Sweep) 的老年代收集器。 其最大特点是:在大部分回收阶段,GC 线程与用户线程并发运行,从而显著减少了 Stop-The-World 时间。
但是,由于不进行“压缩(Compact)”,会导致:
- 内存碎片;
- 晋升失败(Promotion Failed);
- 甚至触发 Concurrent Mode Failure(回退到 Serial Old)。
执行阶段详解
| 阶段 | 是否 STW | 描述 |
|---|---|---|
| Initial Mark | 是 | 快速标记与 GC Roots 直接可达的对象。时间极短。 |
| Concurrent Mark | 否 | 与用户线程并发,从 GC Roots 出发递归标记可达对象(使用三色标记法)。 |
| Remark | 是 | 再次暂停用户线程,补标 Concurrent Mark 阶段遗漏的引用变更(通过写后屏障 + 增量更新机制)。 |
| Concurrent Sweep | 否 | 与应用线程并发清理未标记对象(直接释放内存,不整理)。 |
关键技术点
三色标记法(Tri-color Marking)
使用白、灰、黑三种颜色追踪对象可达性;
保证“黑对象不会指向白对象”,防止漏标;
CMS 使用 写后屏障 + 增量更新(Incremental Update) 来维持三色不变式。
增量更新机制(Incremental Update)
在应用线程修改引用时,CMS 会将新引用的目标对象标记为灰色, 避免出现“黑指白”的漏标问题。
if (A is black && A.field = B) mark(B as gray);1
2
优缺点总结
优点:
- 并发执行,用户线程停顿极短;
- 响应快,适合低延迟场景(如交易系统、实时服务)。
缺点:
- 不整理内存,易产生碎片;
- 并发阶段占用 CPU;
- 若清理速度慢于分配速度,会触发 Concurrent Mode Failure;
- 维护成本高,JDK 14 已正式移除。
# G1 收集器
JDK 9 起默认收集器,面向大内存、低延迟场景。
设计目标:可预测的低停顿(-XX:MaxGCPauseMillis)
基本原理
G1(Garbage-First)是一种面向服务端大堆(GB 级)的收集器,它在思想上是 CMS 的继任者,但从实现上是全新的体系结构:
- 使用 Region 化堆结构;
- 通过 SATB 写前屏障 实现并发标记;
- 通过 RSet 实现跨代引用追踪;
- 通过 CSet 精确选择最优回收区域;
- 支持 Evacuation(疏散复制),天然避免内存碎片。
堆结构:Region 化分区
整个堆被分成大量等大小的 Region(1~32MB):
| Region 类型 | 说明 |
|---|---|
| Eden Region | 新生对象的分配区 |
| Survivor Region | 幸存对象的过渡区 |
| Old Region | 长期存活对象 |
| Humongous Region | 存放大对象(> 1/2 Region) |
年轻代与老年代仍存在,但由 若干个 Region 动态组成,不是固定内存块。
核心机制详解
| 机制 | 作用 |
|---|---|
| RSet(Remembered Set) | 每个 Region 记录外部引用来源,避免全堆扫描 |
| CSet(Collection Set) | 每次 GC 挑选“垃圾比例高”的 Region 优先回收 |
| SATB(Snapshot-At-The-Beginning) | 写前屏障机制,确保并发标记期间不漏标 |
主要阶段流程
| 阶段 | 是否 STW | 说明 |
|---|---|---|
| Young GC(STW) | ✅ | 回收 Eden Region,将活对象疏散到 Survivor / Old。 |
| Concurrent Mark | 🚫 | 全堆并发标记(基于三色标记 + SATB 写前屏障)。 |
| Remark(STW) | ✅ | 补标阶段,清理 SATB buffer,保证完整性。 |
| Cleanup / Mixed GC | ✅ + 🚫 | 统计各 Region 存活率;选择“垃圾多”的 Old Region 一起混合回收。 |
| Evacuation(疏散复制) | ✅ | 复制活对象到新的 Region,实现天然压缩。 |
优点与特点总结
优势:
- 自动控制暂停时间;
- 不产生内存碎片;
- 支持多核并发标记;
- 分区粒度精细,可精确控制回收开销;
- 适合高并发、大堆、低延迟场景。
不足:
- 实现复杂;
- 元数据维护(RSet)占用额外内存;
- 在堆较小或低核环境下性能不如 Parallel。
# 三、垃圾收集器分类总结
| 分类 | 并行性 | 是否并发 | 主要算法 | 典型场景 |
|---|---|---|---|---|
| Serial / Serial Old | 单线程 | 否 | 复制 / 标记整理 | 小堆、单核 |
| ParNew / CMS | 多线程 + 并发 | 是 | 复制 / 标记清除 | 低延迟 |
| Parallel Scavenge / Parallel Old | 多线程 | 否 | 复制 / 标记整理 | 吞吐量优先 |
| G1 | 并行 + 并发 | 是 | 标记-整理(Region化) | 大堆、低延迟 |