深入理解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)至关重要。

相关文章
|
15天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
10天前
|
缓存 并行计算 PyTorch
144_推理时延优化:Profiling与瓶颈分析 - 使用PyTorch Profiler诊断推理延迟,优化矩阵运算的独特瓶颈
在2025年的大模型时代,推理时延优化已经成为部署LLM服务的关键挑战之一。随着模型规模的不断扩大(从数亿参数到数千亿甚至万亿参数),即使在最先进的硬件上,推理延迟也常常成为用户体验和系统吞吐量的主要瓶颈。
349 147
|
10天前
|
机器学习/深度学习 存储 缓存
92_自我反思提示:输出迭代优化
在大型语言模型(LLM)应用日益普及的今天,如何持续提升模型输出质量成为了业界关注的核心问题。传统的提示工程方法往往依赖一次性输入输出,难以应对复杂任务中的多轮优化需求。2025年,自我反思提示技术(Self-Reflection Prompting)作为提示工程的前沿方向,正在改变我们与LLM交互的方式。这项技术通过模拟人类的自我反思认知过程,让模型能够对自身输出进行评估、反馈和优化,从而实现输出质量的持续提升。
400 136
|
4天前
|
人工智能 移动开发 自然语言处理
阿里云百炼产品月刊【2025年9月】
本月通义千问模型大升级,新增多模态、语音、视频生成等高性能模型,支持图文理解、端到端视频生成。官网改版上线全新体验中心,推出高代码应用与智能体多模态知识融合,RAG能力增强,助力企业高效部署AI应用。
255 1
|
14天前
|
存储 人工智能 搜索推荐
终身学习型智能体
当前人工智能前沿研究的一个重要方向:构建能够自主学习、调用工具、积累经验的小型智能体(Agent)。 我们可以称这种系统为“终身学习型智能体”或“自适应认知代理”。它的设计理念就是: 不靠庞大的内置知识取胜,而是依靠高效的推理能力 + 动态获取知识的能力 + 经验积累机制。
405 135
|
14天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
531 133
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
14天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
545 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)