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),并基于 三色标记法 来完成对象可达性分析。整个回收过程通常分为五个阶段:

  1. 初始标记(Initial Mark)
    触发 STW,仅标记 GC Roots 直接引用的对象,由于对象数量较少,因此执行速度很快。
  2. 并发标记(Concurrent Mark)
    与用户线程并发执行,遍历整个对象图,标记所有可达对象。
  3. 重新标记(Remark)
    再次触发 STW,用于修正并发标记期间由于对象引用变化而产生的偏差。
  4. 并发清理(Concurrent Sweep)
    与用户线程并发执行,清理未被标记的对象。
  5. 并发重置(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 对延迟敏感系统收益不明显,但会增加运行时复杂度。

文章目录

随心笔记

技术无止境 创新不停驻