Skip to content

常见垃圾收集器

1. 这是什么

垃圾收集器是在 GC 算法基础上实现的具体回收器。
它们面向不同目标,比如吞吐优先、低停顿优先或更大堆支持。

一句话理解:

  • GC 算法回答的是“怎么回收”
  • 垃圾收集器回答的是“在真实 JVM 里怎么把这些算法组织起来”

2. 为什么重要

不同业务场景对这些指标的要求并不一样:

  • 停顿时间
  • 吞吐量
  • 堆大小
  • CPU 资源占用

理解收集器差异,才能做出更合理的选择。
否则就很容易出现这种情况:

  • 盲目追求“最新”
  • 或者长期抱着不适合当前业务的收集器不放

3. 先建立直觉:没有“放之四海而皆准”的最佳收集器

垃圾收集器之间的差异,本质上是在不同目标之间做权衡,例如:

  • 更高吞吐
  • 更低停顿
  • 更大堆下可接受的延迟
  • 更可预测的响应时间

所以你要避免一种误区:

  • 把“新不新”当成唯一标准

真正的问题是:

  • 这个收集器适不适合你的业务延迟目标和资源条件

4. 核心内容

4.1 Serial

Serial 收集器可以理解成:

  • 单线程执行 GC

特点:

  • 实现简单
  • 额外开销低
  • Stop-The-World 停顿期间只有一个 GC 线程工作

适合:

  • 小堆
  • 单机简单程序
  • 对吞吐和停顿要求不高的场景

4.2 Parallel

Parallel 收集器更偏向:

  • 吞吐优先

特点:

  • GC 阶段利用多线程并行回收
  • 更适合批处理、计算型等更关注总体吞吐的场景

适合:

  • 不特别追求极低停顿
  • 更重视总体处理量

4.3 CMS

CMS(Concurrent Mark Sweep)是历史上非常重要的低停顿收集器。
它的价值在于:

  • 让老年代回收更偏向并发进行
  • 尽量缩短停顿时间

但要注意:

  • CMS 已经是历史收集器
  • 在现代 JDK 中已被移除,不再是主流选择

学习它的意义主要在于:

  • 理解低停顿收集器的发展脉络
  • 理解为什么后来会有 G1、ZGC 这类更现代方案

4.4 G1

G1 可以理解成:

  • 面向服务端通用场景的现代收集器

它的重要特点是:

  • 把堆划分为多个 Region
  • 更强调可预测停顿目标
  • 能同时兼顾较大堆和较平衡的表现

在很多现代 JDK 环境里,它是非常常见的默认或首选候选。

适合:

  • 通用服务端应用
  • 希望在吞吐和停顿之间取得较均衡表现

4.5 ZGC

ZGC 更偏向:

  • 超低停顿
  • 大堆场景

特点:

  • 把停顿时间控制得更低
  • 更适合对响应延迟极其敏感的应用

但也要注意:

  • 它不是“所有场景都自动最好”
  • 仍然要结合硬件、JDK 版本和业务目标判断

5. 常见收集器怎么对比

收集器更偏向的目标典型印象
Serial简单、小堆单线程回收,容易理解
Parallel吞吐优先关注总体处理能力
CMS低停顿(历史方案)重要但已逐步退出主流
G1平衡型、可预测停顿现代服务端常见选择
ZGC超低停顿更适合低延迟和大堆

6. 学习重点

这一章最重要的是掌握这些判断:

  • 收集器不是越新越一定适合
  • 吞吐优先和低延迟优先通常很难同时极致满足
  • G1 是现代通用型很强的选择
  • ZGC 更偏低停顿目标
  • CMS 的价值更多在理解历史演进

7. 常见问题

7.1 盲目追求最新收集器

如果业务规模不大、延迟目标也不严苛,盲目切换未必有收益。

7.2 不结合硬件和业务延迟要求做判断

收集器选择从来不是脱离场景的“理论最优”问题。

7.3 把收集器问题和代码问题完全割裂

垃圾收集器再先进,也救不了:

  • 对象疯狂膨胀
  • 生命周期设计极差
  • 内存泄漏

8. 动手验证

这一节可以直接操作,不需要自己额外写复杂代码。

8.1 先确认当前 JDK 可用收集器

你可以直接运行这些命令:

bash
java -XX:+UseSerialGC -Xlog:gc -version
java -XX:+UseParallelGC -Xlog:gc -version
java -XX:+UseG1GC -Xlog:gc -version
java -XX:+UseZGC -Xlog:gc -version

8.2 你应该观察到什么

在当前环境里,我实际验证到了这些结果:

text
Using Serial
Using Parallel
Using G1
Using The Z Garbage Collector

它们会出现在对应命令输出的 GC 日志里。

8.3 每一条命令在验证什么

  • UseSerialGC:说明 JVM 确实切到了 Serial 收集器
  • UseParallelGC:说明 JVM 确实切到了 Parallel 收集器
  • UseG1GC:说明 JVM 确实切到了 G1
  • UseZGC:说明当前 JDK 环境支持 ZGC,并且能成功启用

8.4 如果你想进一步观察收集行为

可以配合一个简单程序和 GC 日志一起看,例如:

bash
java -XX:+UseG1GC -Xms32m -Xmx32m -Xlog:gc* SomeDemo

这样你就不只是看到“启用了谁”,还可以继续观察:

  • GC 日志格式
  • 停顿阶段
  • 堆变化

8.5 关于 CMS 的特别说明

如果你在现代 JDK 里尝试直接启用 CMS,可能会发现:

  • 参数不再可用
  • 或行为与旧版本资料不一致

这正好说明:

  • 学习 CMS 更应该站在“历史理解”和“原理对比”上
  • 不要机械照搬旧资料到新版本生产环境

9. 练习建议

下面这些练习做完,这一章会更扎实:

  • 整理一张常见收集器对比表
  • 用相同程序分别切换 G1、Parallel、Serial,观察日志差异
  • 总结自己业务更在意吞吐还是延迟
  • 结合当前 JDK 版本重新审视旧文档里的 CMS 经验

10. 自测问题

  • CMSG1ZGC 各自更偏向解决什么问题?
  • 吞吐优先和低延迟优先为什么难以同时极致满足?
  • 为什么垃圾收集器选择必须结合场景?
  • 为什么说 CMS 在现代 JDK 中更多是历史理解对象?

11. 自测核对要点

如果你的回答能覆盖下面这些点,说明这一章基本掌握到位了:

  • 收集器是算法在 JVM 中的具体工程实现
  • Serial 偏简单,Parallel 偏吞吐,G1 偏平衡,ZGC 偏低停顿
  • 收集器选择必须结合延迟目标、吞吐目标和堆规模
  • CMS 很重要,但在现代 JDK 中已经不是主流生产选择