深入理解JVM《CMS收集器详解》

简介: CMS收集器以降低停顿时间为目标,通过并发标记-清除实现低延迟,适用于交互式应用。其采用三色标记法与增量更新确保并发安全,利用卡表优化重新标记效率。虽存在浮动垃圾、内存碎片及资源敏感等缺陷,且已被G1、ZGC取代,但其核心思想对现代GC仍具重要参考价值。

核心目标与设计理念

CMS(Concurrent Mark-Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合那些需要与用户交互的互联网或B/S架构的服务端应用,良好的响应速度能提升用户体验。

CMS的设计理念是:让垃圾收集线程与用户线程(大部分时间)同时工作。它并非追求单次GC的速度最快,而是通过并发,将原本一个长时间的“Stop-The-World”停顿,拆分成多个极短的停顿和长时间的并发操作,从而从整体上降低应用的停顿时间。

CMS的执行过程

CMS的运作过程比之前的收集器更复杂,分为以下四个核心步骤:

1. 初始标记(Initial Mark)

  • 任务:仅仅标记一下GC Roots能直接关联到的对象(即第一层对象)。这个过程速度极快。
  • 状态:需要 “Stop The World” (STW)。但因其只标记直接关联的对象,所以停顿时间非常短。

2. 并发标记(Concurrent Mark)

  • 任务:从“初始标记”阶段标记的对象开始,进行可达性分析遍历整个对象图。这个过程耗时较长。
  • 状态并发执行。垃圾收集线程与用户线程一起工作。这是CMS实现低延迟的关键。

3. 重新标记(Remark)

  • 任务:修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。(具体原因见下文“三色标记法”)。
  • 状态:需要 “Stop The World” (STW)。这个阶段的停顿时间通常会比初始标记阶段稍长,但远低于并发标记的时间。通过多种优化手段(如增量更新、预清理),可以缩短此时间。

4. 并发清除(Concurrent Sweep)

  • 任务:清理删除掉标记阶段判断的已经死亡的对象,释放其占用的空间。
  • 状态并发执行。垃圾收集线程与用户线程一起工作。

下图直观地展示了CMS收集器四个阶段的工作流程及其与用户线程的并发/STW关系:

核心技术:三色标记法与增量更新

CMS的并发标记阶段之所以能安全地与用户线程并发,其理论基础是三色标记法(Tri-color Marking)。它将所有对象分为三种颜色:

  • 白色:表示对象尚未被垃圾收集器访问过。在可达性分析开始时,所有对象都是白色。在分析结束后,仍然是白色的对象,即代表不可达,会被回收。
  • 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经被扫描过。黑色对象是安全存活的,如果有其他引用指向了黑色对象,无需重新扫描。黑色对象不可能直接(不经过灰色对象)指向白色对象。
  • 灰色:表示对象已经被垃圾收集器访问过,但这个对象至少还有一个引用没有被扫描过。灰色对象是分析过程的中间状态。

并发标记时,收集器从灰色对象集合中取出对象开始扫描。扫描的过程就是将灰色对象涂黑,并将其直接引用的白色对象涂灰的过程。

并发标记带来的问题:在收集器标记的过程中,用户线程同时在修改引用关系,这会导致两种主要问题:

  1. 原本消亡的对象被误标为存活(浮动垃圾):可以容忍,下次GC再清理。
  2. 原本存活的对象被误标为消亡(对象消失):这是严重bug,绝对不允许发生。

对象消失需要同时满足以下两个条件:

  • 条件一:赋值器插入了一条或多条从黑色对象到白色对象的新引用。
  • 条件二:赋值器删除了全部从灰色对象指向该白色对象的直接或间接引用。

为了解决“对象消失”问题,CMS在重新标记阶段使用了 增量更新(Incremental Update) 算法。

  • 原理:当黑色对象插入了新的指向白色对象的引用时(破坏条件一),CMS会将这个插入操作记录下来(JVM使用写屏障实现)。在重新标记阶段,会将这些记录过的黑色对象重新变为灰色对象,然后以这些灰色对象为根,重新扫描一次。这样可以确保那些新被黑色对象引用的白色对象不会被错误回收。
  • 简单理解:“无论引用关系删除与否,都会按照刚刚开始扫描的那一刻的对象图快照来进行搜索”

卡表(Card Table)的作用

为了支持高效的重新标记,避免在重新标记阶段重新扫描整个老年代,HotSpot虚拟机使用了卡表(Card Table) 技术。

  • 是什么:卡表是一个字节数组,其中的每个元素对应着老年代内存的一块特定大小的内存块(通常为512字节),这个内存块称为“卡页”(Card Page)。
  • 如何工作:如果老年代中的一个对象引用了一个新生代中的对象(这种跨代引用在分代收集中很常见),JVM就会通过写屏障技术,将对应卡页的卡表元素标记为脏(Dirty)
  • 在CMS中的应用:在重新标记阶段,除了遍历GC Roots,只需要遍历卡表中被标记为“脏”的区域,而不用扫描整个老年代。这大大缩短了重新标记阶段的停顿时间。

CMS的优缺点与调优

优点

  • 并发收集:大部分GC工作(标记和清除)与用户线程并发执行,极大地降低了停顿时间。

缺点与解决方案

  1. 对处理器资源非常敏感
  2. 问题:在并发阶段,虽然不会导致用户线程停顿,但却会因为占用了一部分线程(或者说处理器的计算能力)而导致应用程序变慢,总吞吐量降低。CMS默认的回收线程数是 (CPU核心数 + 3) / 4,当CPU核心数不足4个时,CMS对用户程序的影响可能变得很大。
  3. 解决方案:提供足够的CPU资源。
  4. 无法处理“浮动垃圾”(Floating Garbage)
  5. 问题:在并发清除阶段,用户线程还在运行,自然还会产生新的垃圾。这部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只能留到下一次GC时再清理。这就是“浮动垃圾”。
  6. 后果:因此,CMS不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,必须预留一部分空间供并发收集时的程序运行使用。如果预留的内存无法满足程序分配新对象的需要,就会出现一次 “并发失败”(Concurrent Mode Failure),这时虚拟机将不得不启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就会很长。
  7. 解决方案:使用 -XX:CMSInitiatingOccupancyFraction 参数来设定老年代空间的使用阈值。JDK 5默认值为68%,JDK 6默认值为92%。可以根据实际情况调低此值,为浮动垃圾预留足够空间。
  8. 内存碎片问题
  9. 问题:CMS是基于 “标记-清除” 算法的收集器,这意味着收集结束后会产生大量的内存碎片。
  10. 后果:当无法找到足够大的连续空间来分配当前对象时,不得不提前触发一次Full GC。
  11. 解决方案
  12. 使用 -XX:+UseCMSCompactAtFullCollection 参数(默认开启),在Full GC时开启内存碎片的合并整理过程。但这个整理过程是STW的。
  13. 使用 -XX:CMSFullGCsBeforeCompaction 参数(默认值为0),用于设定执行多少次不整理空间的Full GC后,跟着来一次带整理的。

总结

CMS是一款划时代的老年代收集器,它首次实现了GC线程与用户线程的大规模并发工作,极大地降低了延迟。然而,它并非一款“全能”收集器,其并发失败内存碎片的缺点在堆内存巨大、对象分配频繁的应用中会显得尤为突出。随着官方将发展重心转移到更加先进的G1ZGC等收集器上,CMS已在JDK 9中被标记为废弃(Deprecated),并在JDK 14中被移除(JEP 363)。但理解其工作原理,特别是三色标记法卡表等技术,对于理解所有现代并发收集器(如G1)至关重要。

相关文章
|
1月前
|
监控 算法 Java
深入理解JVM《G1垃圾收集器:面向局部收集与停顿模型的里程碑》
G1收集器是JDK 9+默认的高性能垃圾回收器,采用Region分区模型,实现可预测停顿时间。它通过RSet跟踪跨区引用,结合SATB算法确保并发标记准确性,兼顾低延迟与高吞吐,适用于大内存多核场景。
|
30天前
|
人工智能 算法 安全
AI + 热成像技术在动火作业风险防控中的实现路径
融合AI视觉与热成像技术,构建动火作业安全管控体系。通过定制化易燃物识别、计算机视觉测距、红外温度监测与多源图像融合,实现风险目标精准识别、安全距离实时预警、高温火源智能捕捉,并结合小程序“即拍即查”与后端闭环管理平台,完成隐患从发现到整改的全流程追溯,提升工业现场安全管理智能化水平。
162 10
|
1月前
|
存储 算法 安全
《Java集合核心HashMap:深入剖析其原理、陷阱与性能优化》
HashMap是Java中最常用的Map实现,基于哈希表提供近乎O(1)的存取效率。其核心为“数组+链表+红黑树”结构,通过扰动哈希、&运算索引、扩容机制等实现高效操作。但线程不安全,需注意Key的不可变性与合理初始化容量。深入理解其原理,有助于写出高性能代码,避免常见陷阱。
|
1月前
|
监控 算法 Java
深入理解JVM《垃圾收集(GC)机制与算法 - 宇宙的清洁工》
Java通过垃圾收集(GC)实现自动内存管理,避免手动释放内存导致的泄漏或崩溃。主流JVM采用可达性分析算法判断对象生死,结合分代收集理论,使用标记-清除、复制、标记-整理等算法回收内存。G1、ZGC等现代收集器进一步提升性能与停顿控制。
|
1月前
|
人工智能 自然语言处理 安全
氛围编程陷阱:为什么AI生成代码正在制造大量"伪开发者"
AI兴起催生“氛围编程”——用自然语言生成代码,看似高效实则陷阱。它让人跳过编程基本功,沦为只会提示、不懂原理的“中间商”。真实案例显示,此类项目易崩溃、难维护,安全漏洞频出。AI是技能倍增器,非替代品;真正强大的开发者,永远是那些基础扎实、能独立解决问题的人。
154 11
氛围编程陷阱:为什么AI生成代码正在制造大量"伪开发者"
|
1月前
|
Java API 开发者
告别“线程泄露”:《聊聊如何优雅地关闭线程池》
本文深入讲解Java线程池优雅关闭的核心方法与最佳实践,通过shutdown()、awaitTermination()和shutdownNow()的组合使用,确保任务不丢失、线程不泄露,助力构建高可靠并发应用。
|
1月前
|
传感器 人工智能 算法
数字孪生智慧水务系统,三维立体平台,沃思智能
智慧水务系统融合物联网、数字孪生与AI技术,实现供水全流程智能监测、预测性维护与动态优化。通过实时数据采集与三维建模,提升漏损控制、节能降耗与应急响应能力,推动水务管理从经验驱动迈向数据驱动,助力城市水资源精细化、可持续化管理。
431 143
|
4天前
|
机器学习/深度学习 人工智能 自然语言处理
GPT与BERT深度解析:Transformer的双子星架构
GPT基于Transformer解码器,擅长文本生成;BERT基于编码器,专注文本理解。二者在架构、注意力机制和训练目标上差异显著,分别适用于生成与理解任务,体现了AI智能的多元化发展。
|
4天前
|
机器学习/深度学习 存储 自然语言处理
从文字到向量:Transformer的语言数字化之旅
向量化是将文字转化为数学向量的过程,使计算机能理解语义。通过分词、构建词汇表、词嵌入与位置编码,文本被映射到高维空间,实现语义相似度计算、搜索、分类等智能处理,是NLP的核心基础。
|
4天前
|
存储 机器学习/深度学习 自然语言处理
Transformer参数规模深度解析:从模型聪明说到实际影响
Transformer参数规模显著影响模型能力,参数越多,知识容量与模式识别能力越强,但存在边际效应和过拟合风险。现代大模型通过混合专家、量化压缩等技术提升参数效率,未来趋势是优化参数使用而非盲目扩大规模,实现性能与效率的平衡。(238字)