JVM GC 知识点
JVM 中常见的垃圾收集器主要包括 Serial、Parallel、ParNew、CMS、G1 以及 ZGC 等。不同收集器在 并发能力、停顿时间、吞吐量以及适用场景 上各不相同。下面对常见的几种垃圾收集器进行整理说明
Serial 收集器
(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial 收集器是 JVM 中最基础、历史最悠久的垃圾收集器,它采用 单线程方式执行垃圾回收。在进行垃圾回收时,JVM 会触发 STW(Stop The World),即暂停所有用户线程,直到垃圾回收完成后才恢复业务线程运行。
在算法实现上,Serial 收集器在 新生代使用标记-复制算法(Copying),而在 老年代使用标记-整理算法(Mark-Compact)。由于整个回收过程只有一个 GC 线程,因此实现简单且稳定,但在堆内存较大或 CPU 核心较多的情况下,垃圾回收效率会明显下降。
因此 Serial 收集器通常适用于 内存规模较小、CPU 核心较少的应用环境,例如早期的 Client 模式 JVM 或一些资源受限的系统。
Parallel Scavenge 收集器
(-XX:+UseParallelGC -XX:+UseParallelOldGC)
Parallel Scavenge 收集器是 Serial 收集器的 多线程版本,它的核心目标是 提高系统吞吐量(Throughput)。在垃圾回收时,会使用多个 GC 线程并行执行,从而提高整体回收效率。
默认情况下,GC 线程数量与 CPU 核数基本一致,也可以通过参数 -XX 手动指定线程数量。Parallel 收集器在算法上与 Serial 收集器基本一致,即 新生代使用标记-复制算法,老年代使用标记-整理算法。
由于 Parallel 收集器更关注 吞吐量而非停顿时间,因此非常适合 后台计算型服务或批处理系统。在一些堆内存规模中等(例如 3~4GB 左右)的应用场景中,该收集器可以获得较好的性能表现。
ParNew 收集器
(-XX:+UseParNewGC)
ParNew 收集器可以理解为 Serial 收集器的多线程版本,但它的主要用途并不是单独使用,而是 与 CMS 收集器配合使用。ParNew 负责 新生代的垃圾回收,而 CMS 则负责 老年代的垃圾回收。
在实现方式上,ParNew 采用 多线程并行回收机制,并且在新生代中仍然使用 标记-复制算法。由于 CMS 是一个以低停顿时间为目标的收集器,而 Serial 收集器是单线程的,因此在实际使用 CMS 时通常会搭配 ParNew 来提高新生代回收效率。
这种组合(ParNew + CMS)在 早期 Web 服务和对停顿时间较敏感的系统中使用较多。
CMS 收集器
(-XX:+UseConcMarkSweepGC)
CMS(Concurrent Mark Sweep)收集器主要用于 老年代垃圾回收,其设计目标是 尽可能减少垃圾回收时的停顿时间。CMS 的一个重要特点是 允许 GC 线程与用户线程并发执行,从而减少应用暂停时间。
CMS 使用 标记-清除算法(Mark-Sweep),并基于 三色标记法 来完成对象可达性分析。整个回收过程通常分为五个阶段:
- 初始标记(Initial Mark)
触发 STW,仅标记 GC Roots 直接引用的对象,由于对象数量较少,因此执行速度很快。 - 并发标记(Concurrent Mark)
与用户线程并发执行,遍历整个对象图,标记所有可达对象。 - 重新标记(Remark)
再次触发 STW,用于修正并发标记期间由于对象引用变化而产生的偏差。 - 并发清理(Concurrent Sweep)
与用户线程并发执行,清理未被标记的对象。 - 并发重置(Concurrent Reset)
重置 CMS 的内部状态,为下一次 GC 做准备。
CMS流程图:

CMS把回收过程从一个步骤拆分为多个步骤,把标记和清理都进行了拆分,把最耗时的标记对象步骤进行与用户线程并发进行,从而让应用程序几乎感知不到STW的停顿时间,唯独只有初始标记时与重新标记时有很少的STW停顿时间,其他流程都是并发进行。所以CMS达到了最短回收停顿时间目标,最短回收停顿时间不代表整个回收流程时间要小于其他垃圾收集器,其实CMS整体回收时间是要大与其他垃圾回收器的,但是CMS的停顿时间是最小的。
G1 收集器
(-XX:+UseG1GC)
G1(Garbage First)收集器是 JDK9 之后默认的垃圾收集器,主要用于 大内存服务器环境。与传统垃圾收集器按照新生代和老年代进行物理划分不同,G1 将整个堆划分为多个 Region(区域)。
每个 Region 可以根据需要动态地作为 Eden、Survivor 或 Old 区域使用。G1 会统计每个 Region 中的垃圾比例,并优先回收 垃圾最多、回收收益最高的 Region,这也是 “Garbage First” 名称的来源。
G1 的另一个重要特点是 可预测的停顿时间模型。通过参数 -XX 可以设定期望的最大停顿时间,G1 会根据该目标自动调整回收策略。
整体回收流程包括 初始标记、并发标记、最终标记以及 Region 回收 等阶段。相比 CMS,G1 在减少内存碎片、支持大堆内存以及控制停顿时间方面表现更加优秀,因此在现代 JVM 环境中被广泛使用。
ZGC 收集器
ZGC 是 JDK11 引入的一种低延迟垃圾收集器,其核心目标是 实现毫秒级的 GC 停顿时间。在大多数情况下,ZGC 的 STW 停顿时间可以控制在 10ms 以内,即使在 TB 级别堆内存下也能保持稳定。
ZGC 的实现依赖于几项关键技术,包括 染色指针(Colored Pointer)、读屏障(Load Barrier) 以及基于 Region 的 ZPage 内存管理机制。通过这些技术,ZGC 可以在大部分时间内与用户线程并发执行垃圾回收。
ZGC 的主要回收过程包括 并发标记、并发重定位以及并发引用修复。由于绝大多数工作都在并发阶段完成,因此对应用线程的影响非常小。
因此 ZGC 非常适用于 超大内存服务器、实时系统以及金融交易等对延迟极其敏感的应用场景。
常见 GC 收集器对比
- Serial:单线程回收,适合小型应用
- Parallel:多线程回收,吞吐量优先
- ParNew + CMS:低停顿方案(JDK8 常见)
- G1:现代 JVM 默认收集器,适合大内存服务
- ZGC:超低延迟垃圾收集器,适合 TB 级内存系统
| 收集器 | 类型 | 线程模式 | 主要算法 | 停顿特点 | 适用场景 |
|---|---|---|---|---|---|
| Serial | 新生代 + 老年代 | 单线程 | 复制 + 标记整理 | STW 时间较长 | 小型应用、单核 CPU |
| Parallel Scavenge | 新生代 | 多线程 | 复制算法 | 吞吐量优先 | 后台计算、批处理任务 |
| Parallel Old | 老年代 | 多线程 | 标记整理 | 吞吐量优先 | 高吞吐服务 |
| ParNew | 新生代 | 多线程 | 复制算法 | 停顿较短 | 配合 CMS 使用 |
| CMS | 老年代 | 并发 | 标记清除 | 低停顿 | Web 服务、低延迟系统 |
| G1 | 全堆(Region) | 并发 + 并行 | 复制 + 标记整理 | 可预测停顿 | 大内存服务器 |
| ZGC | 全堆(Region) | 高度并发 | 标记 + 重定位 | <10ms 停顿 | 超大堆内存、低延迟系统 |
扩展知识点
Golang GC 基本机制
Go 从 1.5 版本开始引入三色标记并发 GC(Tri-color Mark and Sweep),并在后续版本持续优化,目前(Go 1.22+)已经形成一套成熟的 并发标记 + 并发清理 + 写屏障(Write Barrier) 的 GC 体系。
Golang GC 的核心特点包括:
- 三色标记算法(Tri-color Marking)
- 并发标记(Concurrent Mark)
- 写屏障(Write Barrier)保证对象引用一致性
- STW 时间极短(通常 < 1ms)
- 自动触发 GC(基于堆增长比例)
Golang GC 的主要流程如下
STW Start
- 暂停所有 Goroutine
- 初始化 GC Root
并发标记(Concurrent Mark)
- GC 与用户 Goroutine 并发执行
- 使用三色标记遍历对象引用
标记终止(Mark Termination)
- 短暂 STW
- 确保所有对象标记完成
并发清理(Concurrent Sweep)
- 回收未标记对象
- 与程序并发运行
JVM GC 与 Golang GC 的核心差异
JVM 采用 多种垃圾收集器(Serial、CMS、G1、ZGC 等)并支持分代回收机制,通过不同算法与参数组合来适配不同场景;而 Golang 从设计之初就采用 统一的三色标记并发 GC(Tri-color Mark & Sweep)体系,不做分代,通过并发标记、写屏障和极短 STW 来实现低延迟回收,因此整体结构更简单、调优成本更低。
为什么 Go 不做分代 GC。
因为 Go 团队认为:分代 GC 对延迟敏感系统收益不明显,但会增加运行时复杂度。