当前位置: 首页 > news >正文

CMS垃圾收集器深度解析:并发低停顿的GC神器

你是否遇到过Java应用因Full GC导致服务卡顿几秒钟,甚至上10秒?在JVM调优的道路上,CMS(Concurrent Mark Sweep)垃圾收集器曾是无数Java工程师的首选方案。本文将深入剖析CMS的工作原理、核心参数与实战配置,助你掌握这一"低停顿神器"的精髓。


📋 文章目录

  • 一、CMS概述:为什么选择它?
  • 二、CMS回收流程深度解析
  • 三、CMS的两种模式:Background vs Foreground
  • 四、核心机制:记忆集、卡表与三色标记
  • 五、CMS常用参数全解
  • 六、CMS线程数计算公式
  • 七、生产环境推荐配置
  • 八、总结与建议

一、CMS概述:为什么选择它?

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它采用的是经典的**“标记-清除算法”**,在JDK 8及之前版本中被广泛使用。

CMS的核心优势

特性说明
并发收集收集器线程与用户线程同时工作,减少停顿
低停顿目标是将停顿时间控制在毫秒级别

CMS的主要缺点

  • 产生大量空间碎片:标记-清除算法的固有问题
  • 并发阶段降低吞吐量:GC线程占用CPU资源

二、CMS回收流程深度解析

CMS的回收过程分为4个核心阶段

1. 初始标记(CMS initial mark)

# 标记GC Roots直接关联对象,不用Tracing,速度很快# JDK7:单线程 JDK8+:默认并行(可通过-XX:+CMSParallelInitialMarkEnabled控制)

2. 并发标记(CMS concurrent mark)

# 进行GC Roots Tracing# 与用户线程并发执行,不停止应用

3. 重新标记(CMS remark)

# 修改并发标记期间因用户程序变动的内容# 需要短暂的STW(Stop The World)

4. 并发清除(CMS concurrent sweep)

# 清除不可达对象回收空间# 同时有新垃圾产生,留着下次清理(称为浮动垃圾)

由于整个过程中,并发标记和并发清除阶段收集器线程可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。


三、CMS的两种模式:Background vs Foreground

Background CMS(正常并发模式)

细化的完整流程实际上包含6个阶段

(1) 初始标记 (2) 并发标记 (3) 并发预处理 (4) 可中止的预处理 (5) 重新标记 (6) 并发清除
为什么需要预处理和可中止预处理?

核心问题:当老年代被回收时,如何判断年轻代对象引用的老年代对象是可达的?

答案是:必须扫描新生代来确定。但全量扫描新生代会很慢,这与CMS"最短停顿时间"的设计理念冲突。

解决方案

  • 先进行一次Young GC,回收新生代
  • 新生代对象变少后,再进行GC Roots扫描
关键参数解读
参数默认值说明
CMSScheduleRemarkEdenSizeThreshold2MEden空间使用超过此值启动可中断预处理
CMSScheduleRemarkEdenPenetration50%Eden空间使用率达到此值时中断预处理
CMSMaxAbortablePrecleanTime5s可中止预处理的最大等待时间
CMSScavengeBeforeRemarkfalseremark前强制进行一次Minor GC

Foreground CMS(并发失败模式)

当并发搜集器不能在老年代填满之前完成不可达对象的回收,或者老年代中有效的空闲内存空间不能满足某个内存分配请求时,会发生并发模式失败(Concurrent Mode Failure)

此时:

  • 应用被暂停(Full STW)
  • 开始单线程全局Full GC
  • 这是ParNew+CMS组合最糟糕的情况
触发条件与避免方法
# 控制老年代达到多少百分比时触发CMS回收-XX:CMSInitiatingOccupancyFraction=70-XX:+UseCMSInitiatingOccupancyOnly# 必须使用此参数,上面的参数才生效

默认触发公式

((100 - MinHeapFreeRatio) + (double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0

按默认值计算,当老年代达到92%时会触发CMS回收。


四、核心机制:记忆集、卡表与三色标记

记忆集(Remembered Set)

进行Young GC时,除了常见的栈引用、静态变量、常量、锁对象、class对象等GC Roots外,如果老年代有对象引用了新生代对象,这些老年代对象也应该加入GC Roots范围。

问题:每次Young GC都扫描整个老年代代价太大。

解决方案:引入记忆集——一种用于记录从非收集区域指向收集区域的指针集合的数据结构。

卡表(Card Table)

卡表是记忆集思想的具体实现。在HotSpot虚拟机中:

卡表是一个字节数组:CARD_TABLE[] 每个元素对应一块512字节(2^9)的内存区域,称为"卡页"

工作原理

  • 只要卡页中有一个对象的字段存在跨代指针,对应卡表元素被置为1(变脏)
  • GC时,筛选本收集区卡表中变脏的元素加入GC Roots

卡表的其他作用

  • 区分引用新生代和并发标记阶段修改过的对象
  • 每个byte有8位,约定每位含义即可区分不同场景

三色标记算法

在并发标记过程中,对象按"是否访问过"标记为三种颜色:

颜色定义状态
黑色对象已被访问,且所有引用都已扫描安全存活
灰色对象已被访问,但至少有一个引用未扫描待处理
白色对象尚未被访问可能可回收

标记过程

  1. 初始时,所有对象在白色集合
  2. GC Roots直接引用对象挪到灰色集合
  3. 从灰色集合取对象,将其引用对象挪到灰色,自身挪到黑色
  4. 重复步骤3,直至灰色集合为空
  5. 白色集合中的对象即为不可达,可回收
多标-浮动垃圾

并发标记过程中,如果部分局部变量(GC Root)被销毁,之前被标记为非垃圾的对象本轮GC不会回收。这部分应该回收但未回收的内存称为**“浮动垃圾”**。

浮动垃圾不会影响GC正确性,只是需要等到下一轮才被清除。

漏标-读写屏障

漏标需同时满足两个条件:

  1. 灰色对象断开了白色对象的引用
  2. 黑色对象重新引用了该白色对象

解决方案

  • 增量更新(Incremental Update):黑色对象插入新指向白色对象的引用时,记录该引用,并发扫描结束后重新扫描
  • 原始快照(SATB):灰色对象删除指向白色对象的引用时,记录该引用,扫描结束后重新扫描

无论插入还是删除,虚拟机的记录操作都通过写屏障实现。

CMS标记压缩算法(MSC)

由于CMS使用标记-清除算法会产生内存碎片,JVM提供了压缩机制:

# 在Full GC后进行碎片整理-XX:+UseCMSCompactAtFullCollection# 执行多少次Full GC后进行压缩整理(默认0,即每次都压缩)-XX:CMSFullGCsBeforeCompaction=0

五、CMS常用参数全解

核心启用参数

# 打开CMS GC收集器(JDK8之前默认Parallel GC,JDK9+默认G1)-XX:+UseConcMarkSweepGC# 年轻代使用多线程并行回收(UseConcMarkSweepGC开启后默认开启)-XX:+UseParNewGC

并发控制参数

# 采用并行标记方式降低停顿(默认开启)-XX:+CMSParallelRemarkEnabled# 并发CMS阶段以多线程执行(默认开启)-XX:+CMSConcurrentMTEnabled# 定义并发CMS过程运行时的线程数-XX:ConcGCThreads=n# 定义CMS过程并行收集的线程数-XX:ParallelGCThreads=m

触发条件参数

# 老年代使用率达到多少时触发CMS(默认68)-XX:CMSInitiatingOccupancyFraction=70# 必须开启此参数,上面的参数才生效-XX:+UseCMSInitiatingOccupancyOnly# CMS默认不对永久代回收,开启此参数可对永久代回收-XX:+CMSClassUnloadingEnabled# 开启增量模式(回收过程更长,但暂停时间更短)-XX:+CMSIncrementalMode

优化参数

# 设置执行多少次Full GC后进行压缩整理(默认0)-XX:CMSFullGCsBeforeCompaction=5# remark前做一次Young GC,减少GC Roots扫描对象数-XX:+CMSScavengeBeforeRemark# 系统GC调用时执行CMS GC,而不是Full GC-XX:+ExplicitGCInvokesConcurrent# 系统GC调用时永久代也被包括进CMS回收范围-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses# 完全忽略系统的GC调用-XX:+DisableExplicitGC# 对类对象数据进行压缩处理(默认开启)-XX:+UseCompressedOops# 设置GC暂停等待时间(毫秒)-XX:MaxGCPauseMillis=200

GC日志参数

-XX:+PrintGCTimeStamps-XX:+PrintGCDetails-XX:+PrintGCDateStamps

六、CMS线程数计算公式

ParallelGCThreads(STW暂停时使用)

CPU核心数 <= 8:ParallelGCThreads = CPU核心数 CPU核心数 > 8:ParallelGCThreads = CPU核心数 * 5/8 + 3(向下取整) 示例: - 4C8G → 4 - 8C16G → 8 - 16核 → 13 - 32核 → 23 - 64核 → 43 - 72核 → 48

ConcGCThreads(GC与业务线程并发时使用)

ConcGCThreads = (ParallelGCThreads + 3) / 4(向下取整) 示例: - ParallelGCThreads = 1~4 → ConcGCThreads = 1 - ParallelGCThreads = 5~8 → ConcGCThreads = 2 - ParallelGCThreads = 13~16 → ConcGCThreads = 4

七、生产环境推荐配置

配置一:8C16G服务器

-Xmx12g-Xms12g-XX:ParallelGCThreads=8-XX:ConcGCThreads=2-XX:+UseConcMarkSweepGC-XX:+CMSClassUnloadingEnabled-XX:+CMSIncrementalMode-XX:+CMSScavengeBeforeRemark-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=70-XX:CMSFullGCsBeforeCompaction=5-XX:MaxGCPauseMillis=100-XX:+ExplicitGCInvokesConcurrent-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses-XX:+PrintGCTimeStamps-XX:+PrintGCDetails-XX:+PrintGCDateStamps

配置二:4C8G服务器

-Xmx6g-Xms6g-XX:ParallelGCThreads=4-XX:ConcGCThreads=1-XX:+UseConcMarkSweepGC-XX:+CMSClassUnloadingEnabled-XX:+CMSIncrementalMode-XX:+CMSScavengeBeforeRemark-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=70-XX:CMSFullGCsBeforeCompaction=5-XX:MaxGCPauseMillis=100-XX:+ExplicitGCInvokesConcurrent-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses-XX:+PrintGCTimeStamps-XX:+PrintGCDetails-XX:+PrintGCDateStamps

配置三:2C4G服务器(不推荐,仅供参考)

-Xmx3g-Xms3g-XX:ParallelGCThreads=2-XX:ConcGCThreads=1-XX:+UseConcMarkSweepGC-XX:+CMSClassUnloadingEnabled-XX:+CMSIncrementalMode-XX:+CMSScavengeBeforeRemark-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=70-XX:CMSFullGCsBeforeCompaction=5-XX:MaxGCPauseMillis=100-XX:+ExplicitGCInvokesConcurrent-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses-XX:+PrintGCTimeStamps-XX:+PrintGCDetails-XX:+PrintGCDateStamps

注意:2C4G环境下,由于线程上下文切换开销较大,CMS性能可能不如默认配置,建议谨慎使用或考虑升级配置。


八、总结与建议

CMS核心要点回顾

  1. 设计理念:以最短回收停顿时间为目标,适合对响应时间敏感的应用
  2. 核心算法:标记-清除,会产生内存碎片
  3. 并发能力:并发标记和并发清除阶段与应用线程并行执行
  4. 风险点:并发模式失败会导致单线程Full GC,停顿时间剧增

使用建议

场景建议
适用对延迟敏感、内存充裕、对象生命周期差异大的应用
不适用内存紧张、CPU资源受限、大内存堆(建议8C16G以下使用)
调优重点控制触发阈值、避免并发失败、合理设置压缩频率

版本建议

  • JDK 8及之前:CMS是低延迟场景的首选
  • JDK 9+:默认使用G1,CMS被标记为Deprecated
  • JDK 14+:CMS已被移除,建议迁移到G1或ZGC

关键词:CMS垃圾收集器, JVM调优, 并发标记清除, GC参数配置, Java性能优化, ParNew CMS, 三色标记, 卡表机制, 低停顿GC

如果本文对你有帮助,欢迎点赞、收藏、关注!有任何JVM调优问题,欢迎在评论区留言讨论。

http://www.jsqmd.com/news/932425/

相关文章:

  • 从ICML 2023高分论文看趋势:想中稿,你的研究该往哪个方向“卷”?
  • 【Sora 2时尚设计黄金公式】:1个结构化Prompt模板+4类面料物理参数校准法
  • 2025-2026年全球工控主板厂家推荐:五大口碑产品评测机器人精准控温市场份额价格 - 品牌推荐
  • G1垃圾收集器源码级深度解析:CSet、RSet与混合回收机制
  • 3个步骤如何用GetQzonehistory找回你的QQ空间青春记忆
  • DLSS Swapper:5分钟完成游戏性能优化的终极指南
  • 【Sora 2广告商业化临界点报告】:为什么92%的营销团队卡在第3关?附Gartner认证评估矩阵
  • 2026年6月热门的扬州燃气燃烧机厂家有哪些推荐榜,一体式低氮燃烧机、分体式比例调节燃烧机、全自动燃气燃烧机、工业级大功率燃烧机、智能变频燃烧机选择指南 - 海棠依旧大
  • LizzieYzy:围棋AI分析的终极免费工具 - 从入门到精通完全指南
  • Sora 2生物动画生成:为什么92%的科研团队仍在用V1旧管线?3个致命兼容盲区正在拖垮你的论文复现效率
  • 2026应届生AI智能降重工具盘点: 学术打磨+逻辑优化哪家强? - 降AI小能手
  • Sora 2培训视频生成必须立刻升级的4项配置——否则下周起将触发OpenAI新内容策略熔断机制
  • 思源宋体TTF字体终极指南:免费商用中文字体的7种样式快速上手
  • 2026年第二季度温州白板笔厂商联系方式深度解析与选型指南 - 2026年企业资讯
  • 告别针孔:用Scaramuzza多项式模型搞定全向相机标定(附Python代码)
  • 2026年5月高纯六氟化硫、电子级六氟化硫及工业级六氟化硫厂家推荐榜与选择指南 - 海棠依旧大
  • Harness 中的请求优先级反转避免协议
  • Linux编译C++项目内存爆了?手把手教你用Swap分区救急(附Ubuntu/CentOS配置命令)
  • 2026杭州靠谱狗粮技术解析:杭州保护肠胃狗粮/杭州全价狗粮/杭州去泪痕狗粮/杭州夹心狗粮/杭州奶糕狗粮/杭州小型犬狗粮/选择指南 - 优质品牌商家
  • 不列颠哥伦比亚大学与亚马逊联合研究揭示如何让AI学会“守规矩“
  • 实战复盘:用SARIMAX预测光伏板温度,我的Matplotlib可视化踩了哪些坑?
  • Sora 2虚拟主播视频伦理风险预警:中宣部《生成式AI内容标识规范》生效前最后48小时应对方案
  • Palworld存档编辑终极指南:安全转换与修改游戏数据
  • 2026年江浙沪压缩机回收服务商排行及选择参考:浙江,上海,江苏,电子厂设备回收/电机回收/电梯回收/电缆回收/选择指南 - 优质品牌商家
  • Sora 2视频物理引擎深度拆解:5大不可绕过的刚体/流体耦合缺陷与工业级修复方案
  • 2026年6月专业的漯河市制造业销售精准获客难题怎么选厂家推荐榜,智能获客系统、SCRM平台、数字营销解决方案、广告投放优化工具厂家选择指南 - 海棠依旧大
  • 超简单!OpenClaw 2.7.8 快速部署步骤(包含安装包)
  • 别再用MLP了!KAN模型实战:用Python复现论文核心,精度提升但速度真慢10倍?
  • 2026年Q2成都考研机构联系服务合规排行一览:成都本地考研辅导电话、成都正规考研集训营、成都线下考研培训、成都考研培训哪家好选择指南 - 优质品牌商家
  • 零基础 Windows 部署 Hermes 实操步骤详解(含安装包)