"面试官问我 CMS 是什么,我脑子一热,差点说成了'内容管理系统'!"这是我一个做了5年 Java 的朋友最近社招面试的真实经历。从这件事我意识到,CMS(Concurrent Mark Sweep)垃圾回收器,虽然老牌,但在很多面试中依然是重点。所以今天,小米就来讲讲我是怎么"跟 CMS 打交道"的------希望能帮到即将跳槽、社招面试的你。
故事的起点:我和 CMS 的第一次"相遇"
还记得那年我第一次维护一个线上老项目,Tomcat 启动参数里赫然写着:
-XX:+UseConcMarkSweepGC
当时的我一脸懵:"这啥?新玩意儿?"
我去查资料才知道,原来它就是传说中的"低延迟垃圾回收器"------CMS,老年代的明星选手,曾一度是互联网公司争相使用的主流 GC 方案。
而这次,也让我开始正式接触 JVM 内存管理的世界。
CMS 的目标是什么?
CMS,全称是 Concurrent Mark Sweep ,中文叫"并发标记-清除"。顾名思义,它的最大特点就是------尽量减少 STW(Stop The World)时间 ,也就是让垃圾回收这件事尽量不打扰业务线程运行。
它的"信仰"是:用户体验优先,延迟比吞吐更重要。
也正因为如此,它被广泛用于对响应时间有要求的 Web 服务类系统,像电商、支付、在线教育等系统中。
CMS 的工作流程(记住这四个阶段)
在我看来,CMS 就像是个不打扰别人的清洁工,它一边让用户用着房间,一边偷偷打扫:
1、初始标记(Initial Mark)
- 短暂停顿(STW)
- 标记所有 GC Root 直接关联的对象
- 非常快,一般几毫秒内完成
2、并发标记(Concurrent Mark)
- 和应用线程同时进行
- 遍历整个对象图,找出存活对象
3、重新标记(Remark)
- 再次暂停(STW)
- 解决并发标记期间产生的对象变动(比如新引用)
- 相对耗时,是整个过程的瓶颈之一
4、并发清除(Concurrent Sweep)
- 清理垃圾对象,回收空间
- 同样是和用户线程一起进行的
总结一句话:只有初始标记和重新标记两个步骤会暂停应用,其它阶段是"边跑边收"。
我踩过的几个 CMS"大坑"
作为一个在生产环境中维护过 CMS 回收器的人,小米必须给你讲几个"实战坑点"。
- 碎片化严重
CMS 是"标记-清除"算法,所以不会像 G1 或 Parallel Old 那样做"压缩"。清理之后留下的是"一个个小碎片"。
这意味着:大对象分配容易失败,即使总内存够,但没有一块连续空间就 GG。
解决方案:加上 -XX:+UseCMSCompactAtFullCollection 参数(默认就是 true),并设定压缩频率 -XX:CMSFullGCsBeforeCompaction=5,让 CMS 每执行几次 Full GC 就顺带整理一次空间。
- 并发失败(Concurrent Mode Failure)
这个问题听上去就让人心慌:回收器还没干完活,堆就满了!
这通常发生在 CMS 清理速度赶不上对象增长速度时,JVM 被迫切换为"Stop The World 的 Serial Old"模式,系统就顿了,甚至会出现卡顿报警。
应对方法:
- 增加老年代大小 -XX:CMSInitiatingOccupancyFraction=75(默认是92)
- 启用 -XX:+UseCMSInitiatingOccupancyOnly 控制 GC 触发时机
- 浮动垃圾(Floating Garbage)
由于 CMS 在并发标记、清理期间,业务线程还在创建新对象,所以这些"新生代对象"在 GC 时根本就没看见。
这些没清理到的对象就叫:浮动垃圾。
这就是为什么 CMS 没法做到"完全回收",它需要依靠多次回收才能慢慢清理干净。
再聊聊 CMS 的参数优化
你面试的时候,光说机制还不够,能讲讲调优,就显得你"干过活"。
常见调优参数:
经验值建议:
- 新生代大小设大一点,避免频繁 Minor GC
- 老年代使用率到 70~75% 左右就开始 GC
- 定期触发 Full GC,压碎片(适度使用)
CMS 的退场:被谁取代了?
2017 年 Oracle 宣布:CMS 将在 JDK 9 后被标记为"deprecated"
它的接班人是谁?那就是 G1(Garbage First) 。
为什么?因为:
- G1 支持压缩(解决了碎片问题)
- G1 更智能(自动按 Region 分区)
- G1 有更短更可控的暂停时间
不过,如果你面试中提到:"虽然 CMS 被 G1 替代,但它依然在很多线上系统中运行,了解它是必要的。"------你绝对是加分项!
面试怎么说 CMS?
来,小米总结一段"答题模板"送你:
CMS 是一种以降低 GC 停顿时间为目标的垃圾回收器,采用标记-清除算法,在初始标记和重新标记阶段会暂停业务线程,其余阶段与业务线程并发执行。适用于对响应时间要求高的场景。缺点是内存碎片化严重、容易出现并发失败。调优时可以设置 CMSInitiatingOccupancyFraction 来提前触发 GC,并定期做 Full GC 以压缩空间。目前 CMS 在 JDK 9 后已被标记为废弃,G1 是其继任者。
简洁、有层次、还带优化经验,一听就是干过的!
建议
如果你还在用 CMS,那一定要:
- 监控老年代使用率
- 留意 GC 日志是否出现 Concurrent Mode Failure
- 考虑是否能升级 G1 或 ZGC(JDK11+)
而如果你准备社招,别光死背概念,要结合故事、项目、场景去说,才能让面试官感受到你的"实战经验"。
END
CMS 是一段经典的 JVM 回忆,也是 Java 调优道路上的"必经之坑"。虽然它已经慢慢退场,但它的机制和思维,依然影响着后来的垃圾回收器设计。
面试,是回顾和总结的好机会。希望今天的这篇文章,能帮你更自信地面对面试中的 JVM 回收问题,也希望你不再被 CMS 的"初始标记"和"重新标记"绕晕啦。
如果你觉得这篇文章有帮助,欢迎点赞、转发 给正在找工作的朋友,也欢迎留言说说你被 CMS"坑"过的经历,我们一起成长!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!