CMS 和 G1 垃圾回收器的区别是什么?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
GC 基础掌握度:面试官不仅仅是想知道你能背出几个区别点,更是想看你是否理解 CMS 和 G1 各自的设计目标、回收算法、以及为什么 G1 能取代 CMS。能把这个逻辑链讲清楚,说明你是真懂了。
-
版本演进意识:CMS 在 JDK 9 被标记为废弃(
Deprecate),JDK 14 正式移除。如果你还在说 "生产环境用 CMS",面试官基本就判定你的知识停留在 JDK 8 了。G1 从 JDK 9 开始成为默认收集器,这个时间线必须清楚。 -
调优实战:能不能说出两个收集器的核心调优参数、适用场景,以及各自已知的缺陷。这决定了面试官认为你是 "纸上谈兵" 还是 "真上过战场"。
核心答案
先给一个全局对比表,一眼看出核心差异:
| 对比维度 | CMS | G1 |
|---|---|---|
| 目标 | 最短停顿时间 | 可预测的停顿时间 |
| 回收算法 | 标记-清除 | 标记-复制(整体看是整理式的) |
| 堆内存布局 | 传统分代(连续的新生代 + 老年代) | Region 化(不再物理隔离新生代和老年代) |
| 回收区域 | 老年代为主 | 优先回收价值最大的 Region |
| 内存碎片 | 有(标记-清除的通病) | 基本没有(复制算法自带整理) |
| 浮动垃圾 | 有(并发阶段产生的新垃圾) | 有,但通过 SATB 快照降低影响 |
| 默认状态 | JDK 9 废弃,JDK 14 移除 | JDK 9 起默认收集器 |
| 适用堆大小 | 6GB 以下 | 6GB 以上,特别适合大堆 |
| 调优难度 | 参数多,调优复杂 | 相对简单,自适应能力强 |
一句话总结:CMS 追求极致低延迟但有内存碎片硬伤,G1 用 Region 化 + 复制算法解决了碎片问题,同时通过停顿时间预测模型实现了可控的 STW。
深度解析
一、CMS:曾经的低延迟王者
CMS(Concurrent Mark Sweep),从名字就能看出来它的两个核心特点:并发标记、清除算法。它的设计目标就一个——最小化 STW(Stop The World)停顿时间。
上图展示了 CMS 的四个核心阶段。关键在于:
- 阶段①初始标记 和 阶段③重新标记 会 STW,但耗时都很短。初始标记只标记 GC Roots 直接关联的对象,速度很快;重新标记是为了修正并发标记期间变动的引用关系。
- 阶段②并发标记 和 阶段④并发清除 是与业务线程并发执行的,耗时长但不影响业务运行。这就是 CMS 低停顿的秘诀——把最耗时的两步放到了并发阶段。
但 CMS 有几个硬伤,面试时一定要提:
- 内存碎片:标记-清除算法不做内存整理,跑久了老年代全是碎片,大对象分配失败时会触发
Concurrent Mode Failure,退化为用 Serial Old 做全堆整理,停顿时间反而更长。这块当年坑了不少人。 - 浮动垃圾:并发清除阶段业务线程还在跑,会产生新的垃圾,只能等下次 GC 才能回收。所以 CMS 不能等老年代满了才开始回收,得预留空间(默认在 68% 时触发,
-XX:CMSInitiatingOccupancyFraction可调)。 - CPU 敏感:并发阶段默认占用
(CPU 核数 + 3) / 4个线程,CPU 核心少的时候对吞吐量影响比较大。
二、G1:Region 化的革命性设计
G1(Garbage First)的核心创新在于打破传统分代的物理隔离,把整个堆划分成大小相等的 Region。
上图展示了 G1 的 Region 化布局。关键要点:
- 每个 Region 大小相同(1MB~32MB,默认按堆总大小自动计算,可通过
-XX:G1HeapRegionSize指定),逻辑上属于 Eden、Survivor、Old 或 Humongous 中的一种,但物理上不再连续。 - Humongous Region:专门存放大对象(超过 Region 大小 50% 的对象)。这是 G1 针对大对象的特殊处理,避免大对象直接进老年代浪费空间。
- 回收时不再全量回收,而是优先回收 "垃圾最多" 的 Region(Garbage First 名字的由来),用有限的停顿时间换取最大的回收收益。
G1 的回收过程分为三个阶段:
上图展示了 G1 的三个核心回收阶段。和 CMS 最大的不同在于 阶段③混合回收:
- Young GC 只回收年轻代,和传统的 Minor GC 类似。
- 并发标记 阶段使用 SATB(Snapshot At The Beginning) 快照技术,比 CMS 的增量更新方式效率更高。简单说就是 GC 开始时拍一个快照,只关心快照时刻存活的对象,并发阶段新产生的引用变动通过写屏障记录下来。
- Mixed GC 是 G1 的杀手锏——它不是回收整个老年代,而是挑选垃圾占比最高的几个 Old Region 进行回收。回收多少个 Region 取决于你设置的停顿时间目标(
-XX:MaxGCPauseMillis,默认 200ms)。G1 会根据历史数据预测每个 Region 的回收耗时,然后在目标时间内尽可能多地回收垃圾。这就是 "Garbage First" 的含义——垃圾最多的 Region 优先回收。
三、CMS vs G1 核心区别对比
这里把面试官最关心的几个维度再深入对比一下:
1. 内存碎片问题
CMS 用标记-清除算法,清除后不整理,碎片会越来越多。虽然可以配 -XX:+CMSCompactAtFullCollection 在 Full GC 时整理,但那次 Full GC 的停顿就很可怕了。
G1 本质上用的是复制算法——把存活对象从一个 Region 复制到另一个 Region,复制过程天然就做了内存整理。所以 G1 基本不会有碎片问题。
2. 停顿时间的可控性
CMS 的停顿时间是 "尽力而为" 的,你没法告诉 CMS "我希望停顿不超过 100ms"。它的停顿时间取决于老年代的大小和对象存活率。
G1 有个参数 -XX:MaxGCPauseMillis(默认 200ms),你可以明确告诉 G1 你期望的停顿时间上限。G1 会根据这个目标和历史数据,动态计算本次 Mixed GC 回收多少个 Region。注意,这是 "目标" 而非 "保证",面试时别把话说死了,说 "G1 通过停顿预测模型尽量将 STW 控制在目标范围内" 更准确。
3. 堆大小的适用范围
CMS 在 6GB 以下的堆表现不错,堆再大的时候,Old GC 的耗时就会明显增加,尤其是触发 Concurrent Mode Failure 后的 Full GC,停顿时间可能达到几秒甚至十几秒。
G1 专门针对大堆设计,6GB 以上甚至几十 GB 的堆都能有不错的表现。Region 化设计让回收粒度更细,不需要每次都扫描整个老年代。
4. 调参复杂度
CMS 要调的参数很多:CMSInitiatingOccupancyFraction、CMSWaitDuration、CMSExpireInterval……调不好性能反而不如 Parallel GC。
G1 相对省心,大多数场景用默认参数就够了。核心参数就那么几个:
# G1 常用调优参数
-XX:+UseG1GC # 启用 G1
-XX:MaxGCPauseMillis=200 # 期望最大停顿时间(默认 200ms)
-XX:G1HeapRegionSize=8m # Region 大小(一般不用调)
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的堆占用阈值(默认 45%)
面试高频追问
-
追问:G1 什么时候会退化为 Full GC?
当 Mixed GC 的回收速度跟不上对象分配速度,老年代被填满时,G1 会退化为使用 Serial Old 做单线程的全堆整理。这时的停顿非常长,和 CMS 的
Concurrent Mode Failure类似。避免的方式是合理设置-XX:MaxGCPauseMillis和-XX:InitiatingHeapOccupancyPercent,让并发标记更早触发。 -
追问:JDK 11 之后还有哪些新的垃圾回收器?
ZGC(Z Garbage Collector)在 JDK 11 实验性引入,JDK 15 正式发布。它的停顿时间可以做到 亚毫秒级(< 1ms),且不随堆大小增长。适合超大堆(TB 级别)和对延迟极度敏感的场景。另外还有 Shenandoah(OpenJDK 的另一个低延迟收集器),停顿时间也能做到 10ms 以内。趋势很明确:未来的 GC 方向就是极低延迟 + 超大堆支持。
-
追问:你们生产环境用的什么垃圾回收器?为什么选它?
这道题面试官是在考察你的实战经验。如果你用的是 JDK 8,可以说 "用的 CMS,因为堆在 4GB 左右,对延迟敏感,CMS 够用。但通过监控发现偶尔会触发
Concurrent Mode Failure,所以也在考虑升级 JDK 切 G1"。如果是 JDK 11+,直接说 G1,然后配合-XX:MaxGCPauseMillis的调优经验就行。
常见面试变体
- "G1 和 CMS 的区别?生产环境用哪个?"
- "为什么 G1 能取代 CMS?"
- "G1 的 Mixed GC 是什么意思?"
- "CMS 有什么缺陷?怎么解决?"
- "JDK 8 和 JDK 11 默认的垃圾回收器有什么区别?"
记忆口诀
CMS:并发标记清除,停顿短但碎片多("快但碎")
G1:Region 分区优先回收,停顿可控无碎片("分而治之,垃圾优先")
选择依据:小堆(< 6GB)追求延迟选 CMS(JDK 8),大堆或 JDK 11+ 选 G1,超低延迟选 ZGC
总结
CMS 和 G1 最核心的区别就三个:内存布局(传统分代 vs Region 化)、回收算法(标记-清除 vs 标记-复制)、停顿可控性(尽力而为 vs 目标驱动)。面试时先抛出这三点,再展开各自的回收流程和优缺点,最后说一下版本演进(CMS 已移除,G1 成默认),基本就覆盖了面试官的所有预期。
