第048篇 常见垃圾回收器:从 CMS 到 G1 的演进思路

简介: 本文深入剖析JVM垃圾回收器的演进本质——并非技术堆砌,而是“吞吐↔停顿”的持续取舍:Serial(小堆低开销)、Parallel(高吞吐/长停顿)、CMS(并发降停顿但碎片/弃用)、G1(Region收益优先/可控停顿)、ZGC/Shenandoah(亚毫秒停顿/吞吐略损)。强调选型需匹配堆规模与业务指标,核心在理解分配速率、存活率与停顿目标的联动关系。

垃圾回收器这题,答出"有 Serial、Parallel、CMS、G1"的人很多,答不出"它们各自在吞吐与停顿之间做了哪些取舍"的人占大多数。而面试官问的恰恰是取舍——因为不同收集器的差异,本质上就是"用吞吐换延迟"这条主线上不同的停靠点。理解这条主线,收集器就不再是一串需要背的名字。

先把结论放在前面:收集器的演进就是一条取舍线。 Serial 只有单线程,零额外开销,适合小堆;Parallel(也叫 Parallel Scavenge + Parallel Old)用多线程并行做标记与复制,吞吐高但停顿集中;CMS 引入并发标记,把大部分工作与应用线程并行、降低停顿,但会产生碎片且有浮动垃圾,已在 JDK 14 弃用;G1 把堆划分为等大小的 Region,按"回收收益排序"优先清理,从而能在给定停顿目标内尽量多回收;ZGC / Shenandoah 借助着色指针与读屏障实现亚毫秒停顿,代价是吞吐略低、可用堆规模受指针空间限制。

机制拆解

讲清 CMS 与 G1 之间的那次转折,这是这题的核心。CMS 的并发标记解决了"标记阶段长时间 STW"的问题,但它有三个残留问题:并发标记期间新对象是"浮动垃圾"(本轮不回收,等下一轮)、标记与清除之间会产生内存碎片、扫描线程与应用线程争抢 CPU。 而 G1 的解法很聪明:它不再要求"回收所有不可达对象",而是接受只回收一部分——把 Region 排成队列,每次挑几个脏 Region 复制回收,用"只清一部分但每一份都很快"换来可控的停顿。这就是 MaxGCPauseMillis 背后的设计哲学:暂停时间是目标,不是承诺。

这些坑的正确绕法

最常见的坑是小堆用 G1 的默认配置反而比 Parallel 更频繁并发周期,停顿更长。 G1 的优势建立在堆足够大、Region 足够多之上;堆小的时候 Region 数量少,每次并发周期要处理的比例反而更高,加上维护记账与写屏障的开销,实际表现可能不如 Parallel 这类专注吞吐的收集器。所以选型不是"新的就是好",而是看堆规模与延迟诉求的匹配度。

其次是追新版本收集器却忽略业务对象分配速率,GC 参数调不出预期效果。 停顿时间不只取决于收集器,还取决于每个周期要回收多少对象,而这由分配速率决定。应用如果每秒分配大量短命对象,收集器再先进也要频繁工作。有效的做法是同时看两个指标:分配速率(分配量/时间)与存活速率(回收后仍然存活的比例);后者高说明对象真的活得久,得从减少持有时长入手,前者高则可以考虑对象池、复用或分批处理。

还有一个更隐蔽的坑:把停顿时间当成唯一指标,忽略了吞吐。 ZGC 停顿很短,但单核性能开销明显,在低端机或需要高吞吐的服务端场景反而不如 G1;而 G1 为了达成停顿目标会做更多工作,CPU 消耗也高于 Parallel。评估要落到"单位时间内完成的业务量"这个综合指标上。

常见垃圾回收器的实践经验是:前提先声明,失败路径给兜底,事故挡在上线前。 落地成几条:先确认业务的延迟与吞吐目标再选收集器;不要动没有证据支持的参数;把 GC 日志与停顿分布纳入监控;Android 上还要考虑 ART 自己的收集器与设备内存等级,不能照搬服务端 JVM 的方案。

代码里见真章

看一段能直接跑的代码,把上面的机制落到具体写法上:

// 常见收集器:演进即"吞吐 ↔ 停顿"的取舍
// Serial:小堆,零额外开销
// Parallel(Scavenge+Old):多线程,吞吐高,停顿集中
// CMS:并发标记,但有浮动垃圾与碎片,JDK14 弃用
// G1:Region 化 + 收益排序,每次只回收一部分,目标是可控停顿
// ZGC/Shenandoah:着色指针 + 读屏障,亚毫秒停顿,吞吐略低
// 关键指标:MaxGCPauseMillis 是目标;分配速率决定每周期要收多少

这段代码值得盯两处:第一处,G1 的注释点明"每次只回收一部分"这个核心机制,它解释了 G1 的停顿控制原理;第二处,最后一行点出分配速率这个常被忽略的变量,提示参数调优的前提是先看业务。面试讲到这一层,基本就稳了。

这题在面试里怎么问、怎么答

"请简单介绍一下常见垃圾回收器,它在 Android 开发中起什么作用?"先给演进线(Serial → Parallel → CMS → G1 → ZGC),再点出每一代解决的核心问题(吞吐 → 停顿 → 碎片 → 可控停顿 → 亚毫秒)。Android 上补充一句:ART 使用自己的收集器(早期是 Concurrent Copying GC,近年引入其他策略),并受设备内存等级与堆配置影响。

"常见垃圾回收器的底层原理是什么?能不能详细说一下?"分两派答。新生代基本是复制算法:把存活对象复制到另一块 Survivor/Eden,天然无碎片、成本与存活量成正比;老年代则在标记清除、标记整理、标记复制之间选。G1 的独到之处是"标记—复制"在 Region 粒度上做,且回收顺序按垃圾量与区域大小估算收益。 ZGC 则把堆做成染色指针的页表式结构,用读屏障来判断对象是否被移动,从而不需要移动对象就能完成并发压缩。讲完这三派,这题可以给高分。

"在使用常见垃圾回收器时遇到过什么问题?"拿真实案例。一个典型案例:某次版本发布后 P99 延迟明显上升,GC 日志显示并发周期变频繁、每次要处理的对象变多;定位到某处新增的列表构建在主线程上反复产生大数组;修复为把构建移到后台并复用缓冲;验证是分配速率下降、停顿恢复到基线。这个案例讲清了"收集器只是执行者,分配速率才是输入"。

"和相关的替代方案相比,有什么优劣?"对比"换收集器"与"减少分配"两条线。结论:延迟问题先查分配速率与存活量,能不改收集器就不改;确实需要更低延迟再上 ZGC;追求吞吐极限的服务场景 Parallel 依然合适。选型时把"小堆用 G1 可能更差"这类代价摆到台面上,再决定是否引入。

再补一个工程上值得讲清的点:如何读 GC 日志来判断问题出在哪。停顿时间长,先看是老年代增长过快(说明存活量高、可能有泄漏)还是并发周期太频繁(说明分配速率高);如果是后者,缩短周期的手段是减少分配而不是换收集器;如果老年代在每次 Full GC 后都显著下降,说明不是泄漏,只是堆不够,可以评估调大;有曲线呈持续爬升且 Full GC 后也不降,才是泄漏的信号。 会读这几条曲线,面试里就能从"知道收集器"跨到"能定位问题"。

顺带说一个容易被问到的高频陷阱:finalize() 与 System.gc()。现代 JVM 已经废弃 finalize(),依赖它释放资源是典型的过时做法,应该用 try-with-resources 或显式的 close();而 System.gc() 只是向 GC 发出一个"建议",实现上完全可以忽略,依赖它回收会得到不确定的行为。 面试里能主动纠正这两个过时做法,通常会明显加分。

给正在准备面试的你

把收集器画成一条时间轴:Serial → Parallel → CMS → G1 → ZGC,每个节点标出"它解决了上一代什么问题"与"付出了什么代价",然后在轴下方画出两条曲线——停顿时间下降、吞吐下降。再在图上标出今天 JVM 的默认选择与 Android 侧 ART 的差异。面试中关于收集器的问题,关键在于能够从原理、应用、踩坑三个层面给出有深度的回答。

再补工程案例与踩坑——应用落点是抓一次线上 GC 日志,画出停顿分布与老年代占用曲线,据此判断是分配速率问题还是泄漏问题,并给出对应的验证与修复方案。

复习时别孤立刷题:垃圾回收基础——收集器解决的是"怎么回收",判定标准与引用分级仍是上一环。

划两句重点:收集器的演进是吞吐与停顿之间的取舍,G1 靠"只回收一部分脏 Region"达成停顿目标;停顿时间不只取决于收集器,更取决于分配速率与存活量,选型前先量这两个指标。

下一篇聊 JVM 调优入门:从 OOM 日志倒推问题——沿着今天这条主线继续往前走。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。你的支持就是这个系列持续更新的动力。

「Android软件开发面试·从入门到精通」连载系列

上一篇:垃圾回收基础:可达性分析与-GC-Roots

下一篇预告:JVM-调优入门:从-OOM-日志倒推问题

有任何问题欢迎在评论区留言交流。

相关文章
|
17天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
8478 24
|
16天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
2817 14
|
15天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2021 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
14天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
10天前
|
人工智能 Linux 开发者
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
Codex是OpenAI推出的AI编程智能体,可读取本地项目、理解需求并自动修改代码。支持桌面GUI、命令行(CLI)及VS Code/Cursor插件三种形态,覆盖可视化操作、终端高效开发与编辑器无缝集成场景,助开发者用自然语言驱动编码全流程。(239字)
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
|
4天前
|
人工智能 JSON Linux
【全网最详细】ComfyUI使用教程:下载+本地部署+配置+工作流搭建一篇搞定(2026最新版)
ComfyUI是一款免费开源的本地AI绘图工具,采用节点式工作流设计,支持文生图、图生图、局部重绘、放大、换脸等多种功能。可离线运行,依赖显卡加速,无需联网。支持自定义流程保存与分享,插件生态丰富,适合进阶用户。(239字)
|
10天前
|
人工智能 JSON 编解码
【2026最新版】ComfyUI本地部署教程,新手也能看懂!
ComfyUI是本地运行的AI绘画工具,采用节点式工作流设计:通过拖拽连接“加载模型”“提示词编码”“采样”“解码”等模块,实现高度可控的文生图。新手推荐使用秋叶整合包,一键启动、内置模型管理与插件安装器,轻松上手。(239字)

热门文章

最新文章