目录

  • 一、CMS
    • 1.1 概述
    • 1.2 内存碎片
    • 1.3 浮动垃圾
    • 1.4 空间预留
    • 1.4 Promotion Failed和Concurrent Mode Failure
    • 1.5 常用参数
  • 二、总结

一、CMS

1.1 概述

  CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,其主要优点就是并发收集、低停顿。适合重视服务响应速度的应用的服务器,基于标记-清除算法,用于老年代。它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次基本上实现了让垃圾收集线程与用户线程同时工作。

  它的收集过程主要分为以下步骤:

  • 初始标记会STW,但只是标记一下GC Roots能直接关联到的对象,速度很快
  • 并发标记:从GC Roots直接关联的对象(初始标记的对象)开始枚举,这个过程在所有步骤中耗时最长,但是不会STW,用户线程和GC线程并发运行。所以在这个阶段,可能会导已经被标记过的对象状态被用户线程再次改变
  • 重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这里涉及一个三色标记算法。会STW,这个阶段花费时间比初始标记长,但是远低于并发标记
  • 并发清除不会STW,GC线程对未标记的对象进行清理(未被标记表示该对象是垃圾),如果在这个期间由于用户线程并发运行产生了新的垃圾,称为”浮动垃圾“,本次GC不会被清理,留待下次GC清理
  • 并发重置:在GC过程中,对象被打了一些标记,这里需要将这些标记重置,不会STW

  所有步骤的切换都需要等到安全点(Safepoint),关于安全点和安全域可参考垃圾收集器与内存分配策略。整个收集过程,只有初始标记和重新标记会STW,真正耗费大量时间的操作都和用户线程并发运行,所以说基本上实现了并发收集。

1.2 内存碎片

  由于CMS采取的是标记-清除算法,所以GC结束可能会导致有大量的内存碎片,给对象分配带来困难。所以CMS提供了参数 -XX:+UseCMSCompactAtFullCollection(默认开启),用于在FullGC后进行内存碎片整理,但是碎片整理需要STW,会影响整体体验。所以还提供了一个参数 -XX:CMSFullGCsBeforeCompaction,用于设置执行多少次不整理碎片的FullGC后,再整理碎片。

1.3 浮动垃圾

  在并发清理过程中,由于用户程序也在运行,就可能导致产生新的垃圾,这时候标记已经结束,进入了清理阶段,所以无法清理这部分垃圾,称之为”浮动垃圾“,只能留待下一次GC清理。在并发标记阶段也会产生类似(或更严重)的情况,比如本是垃圾的对象变为了非垃圾,或者本是非垃圾的对象变为了垃圾。如果非垃圾变为了垃圾,即使把它当做浮动垃圾,在本次GC过程中不再处理,问题也不大。但是如果垃圾变为了非垃圾就不能不管了,如果垃圾收集器回收了一个正在被使用的对象,就太可怕了。会在重新标记阶段去修正,总体的原则就是”另可放过,不可杀错“。

1.4 空间预留

  由于GC线程和用户线程并发运行,那么就需要预留足够的空间给用户线程使用,因此CMS不能等到老年代几乎完全被填满了再进行收集。可以使用参数 -XX:CMSInitiatingOccupancyFraction配置老年代使用达到什么比例时触发FullGC,这个参数在JDK1.6之后的默认值是92。

1.4 Promotion Failed和Concurrent Mode Failure

  在进行Minor GC时,如果存活对象在survivor放不下,会使对象晋升入老年代(或者大对象直接进入老年代),但是老年代可能由于剩余可用内存不足或内存碎片过多(发生过Full GC但是还没有整理内存碎片),导致无法存放新晋升的对象,就会产生Promotion Failed。一句话说就是对象需要晋升到老年代,但是老年代放不下。
  而如果CMS在GC期间,不断有对象晋升到老年代,但是老年代空间不足,可能又需要触发一次Full GC,但是由于此时正在GC,就会导致,”Concurrent Mode Failure“。这个主要可能发生在并发标记和并发清理阶段,因为这些阶段用户线程在并发运行。如果出现Concurrent Mode Failure,虚拟机会启动后备预案,临时启用Serial Old收集器来对老年代进行单线程收集,这样STW的时间就会很长了。
  可以这样理解,产生Promotion Failed可能就需要触发Full GC,而此时CMS正在GC,就会导致Concurrent Mode Failure。

关于临时启用Serial Old的问题,个人猜测是因为CMS主要和Serial和ParNew收集器组合,当然主要是ParNew。但是当时老年代只有三个收集器,分别是CMS、SerialOld和ParallelOld,而ParNew只能和CMS和Serial Old组合,现在CMS不可用,就只能使用Serail Old了,ParNew和Serial都无法与Parallel Old组合使用。

  若系统中的大对象较多,可适当降低CMSInitiatingOccupancyFractio参数的配置,尽量减少Promotion Failed,从而减少Concurrent Mode Failure,但是这也同样意味着Full GC可能会更加频繁。我们要合理配置UseCMSCompactAtFullCollection和CMSFullGCsBeforeCompactio参数,减少内存碎片,同时建立好应用的”内存模型“,尽量保证朝生夕死的对象能在年轻代就被GC掉,减少Full GC的频率。当然,CMSInitiatingOccupancyFraction参数对于控制Full GC同样重要。GC调优本就是一个理论加实践的结合,必须要具体情况具体分析。

1.5 常用参数

  • -XX:+UseConcMarkSweepGC:使用此参数启用CMS
  • -XX:+CMSParallellnitialMarkEnabled:在初始标记阶段使用多线程执行
  • -XX:+CMSParallelRemarkEnabled:在重新标记阶段使用多线程执行
  • -XX:ConcGCThreads:并发的GC线程数,根据CPU数量计算
  • -XX:CMSInitiatingOccupancyFraction:当老年代使用达到这个百分比时触发FullGC(JDK1.6后默认是92)
  • -XX:+UseCMSCompactAtFullCollection:启用在Full GC之后进行内存碎片整理,默认开启
  • -XX:CMSFullGCsBeforeCompaction:表示多少次FullGC之后再整理一次碎片,默认为0,代表每次FullGC后都会进行碎片整理
  • -XX:+CMSScavengeBeforeRemark:在CMS重新标记前进行一次Minor GC
  • -XX:+UseCMSInitiatingOccupancyOnly:只使用CMSInitiatingOccupancyFraction参数配置的阈值,如果不启用此参数,JVM只会在第一次使用配置阈值,后面自动调整

  由于一个老年代的对象可能被年轻代所引用,所以为了避免清理了本还存活的对象,CMS标记会把新生代也算入GC Root的一部分进行可达性分析,如果新生代引用了老年代的对象,那么老年代的这个对象就是存活的,不能被清理。但是CMS在并发标记阶段和用户线程是并发运行的,之前已经标记过的对象的引用关系可能会发生改变,而且新生代对象"大多"都是朝生夕死的,如果在重新标记之前进行一次Minor GC,那么可能会减少不少新生代到老年代的无效引用,从而降低重新标记的开销。可以使用参数-XX:+CMSScavengeBeforeRemark启用在重新标记之前进行一次Minor GC,但是Minor GC也会有开销,这个开销和重新标记的开销如何权衡就要看实际情况了。如果发现重新标记时间过长,可以开启这个参数试试,否则可以不管。
  另外,对于UseCMSInitiatingOccupancyOnly参数,如果没有开启这个参数,虚拟机后面则会根据运行时收集的数据来自动进行调整。比如基于运行时收集的历史数据,CMS后台线程判断出老年代将被填满的时间小于GC可能消耗的时间,那么为了尽量避免Concurrent Mode Failure,会触发一次GC,即使现在老年代占用还没有到OccupancyFraction参数配置的阈值,接着可能会降低OccupancyFraction参数的阈值。通常情况下,如果不是特别确认需要开启该参数,以更加严格的控制GC,那么不开启这个参数,让虚拟机自动帮我们抉择GC的时机会是个不错的选择。

二、总结

  从CMS的实现看来,虽然STW时间变少了,但是整体的GC时间可能还变多了。这个也可以说是用户至上的思想,更加关注用户体验:虽然GC时间变长了,但是用户感受到的STW却变短了。当然,随之而来的就是更加复杂的设计与实现。
  CMS具有超多的配置选项,如果算上垃圾收集器公用的GC参数,CMS能使用的配置参数超过100个(查的资料,没用过这么多~),上面列举的只是在平时的开发优化过程中常用的少数几个,其也在JDK9中被弃用,可能在后面的版本被直接删除。虽然CMS也有诸多缺点,但是它作为HotSpot中第一款真正意义上的并发收集器,第一次实现了让垃圾收集线程与用户线程基本同时工作,其思想对之后越来越优秀的垃圾收集器都有很好的指导作用,它也仍是目前中小堆内存下使用非常广泛的老年代垃圾收集器。

注:本文基于博主个人理解,部分官方描述参考书本,如有错误,感谢指出!

垃圾收集器总结--CMS垃圾收集器相关推荐

  1. 27.垃圾收集器(Serial收集器、ParNew收集器、Parallel收集器、Parallel Old 收集器、CMS收集器、G1收集器、常用的收集器组合)

    27.垃圾收集器 27.1.Serial收集器 27.2.ParNew收集器 27.3.Parallel收集器 27.4.Parallel Old 收集器 27.5.CMS收集器 27.6.G1收集器 ...

  2. (七)JVM成神路之GC分代篇:分代GC器、CMS收集器及YoungGC、FullGC日志剖析

    引言 在<GC基础篇>中曾谈到过分代以及分区回收的概念,但基础篇更多的是建立在GC的一些算法理论上进行高谈阔论,而本篇则重点会对于分代收集器的实现进行全面详解,其中会涵盖串行收集器.并行收 ...

  3. CMS垃圾收集器和G1垃圾收集器

    CMS CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的老年代收集器.CMS收集器与之前的垃圾收集器最大的特点就是它可以并发清除垃圾. 他的工作流程如下: ...

  4. 【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )

    文章目录 一. 分代收集算法 二. 垃圾回收器 / 收集器 ( GC ) 三. 串行收集器 ( Serial ) 四. ParNew 收集器 五. Parallel Scavenge 收集器 六. C ...

  5. JVM实用参数(七)CMS收集器

    原文连接 本文连接  译者: iDestiny  校对:梁海舰 HotSpot JVM的并发标记清理收集器(CMS收集器)的主要目标就是:低应用停顿时间.该目标对于大多数交互式应用很重要,比如web应 ...

  6. CMS收集器和G1收集器,优缺点对比

    点击上方关注"Java后端技术栈" 很多面试题都会涉及CMS收集器和G1收集器,这里面有一个非常重要的知识点:G1只有并发标记才不会stop-the-world,其他都会停下来. ...

  7. 一网打尽!CMS收集器和G1收集器的区别

    CMS 收集器 CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器.它非常符合在注重用户体验的应用上使用. CMS(Concurrent Mark S ...

  8. 垃圾收集器(CMS收集器 , G1收集器…)

    Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,以下讲 ...

  9. 0 full gc时cpu idle_结合GC日志讲讲CMS垃圾收集器

    1 CMS垃圾收集器介绍 CMS(Concurrent Mark Sweep)收集器旨在获取最短回收停顿时间的并发垃圾收集器.CMS基于"标记-清除"算法实现,并发指的是CMS的垃 ...

最新文章

  1. spring suite tool 添加namespace时只有bean一个
  2. php 图片印章_PHP实现中文圆形印章特效_PHP
  3. QPW 企业维度评分表(tf_company_dimesion)
  4. createSQLQuery与createQuery的区别
  5. 华为云VSS漏洞扫描服务之开源组件漏洞检测能力
  6. Think in Java第四版 读书笔记7第13章 字符串
  7. 大数据之-Hadoop完全分布式_rsync案例_差量分发_只把有差异文件进行更新同步到其他服务器---大数据之hadoop工作笔记0032
  8. 系统辨识理论及应用_企业战略分析的理论工具
  9. oracle数据泵功能,Oracle数据泵的使用(1)-Oracle
  10. Golang语言 零基础入门教程
  11. zend studio php调试,手把手教你配置zendstudio的xdebug调试过程
  12. MySQL数据表操作思维导图
  13. 团队作业9——展示博客
  14. 使用studio 3T按日期查询mongodb
  15. java 提取违反顺序_oracle 中 java.sql.SQLException: ORA-01002: 提取违反顺序
  16. html5如何实现无序排列,无序列表让li横排
  17. 字符串常见方法总结: 构造方法、静态方法、 其它方法
  18. JSFuck奇葩的js编码
  19. ubuntu 14.04.5 编译Android 4.4.4 r1源码(最新)
  20. Google 的秘密- PageRank 彻底解说 中文版(二)

热门文章

  1. OpenCV—直线拟合fitLine
  2. java get_JAVA 中get()和get()的用法,和意义?
  3. WPS如何实现整行数据行间随机排序
  4. abs210桥堆的芯片多大,ASEMI-ABS210贴片整流桥
  5. 一文了解二进制和CPU工作原理
  6. 凹凸技术揭秘·羚珑智能设计平台·逐梦设计数智化
  7. 运维工程师使用的运维平台和工具表
  8. 雷迪9000使用说明_标准版DM雷迪操作及维护手册 精品
  9. c语言 英文课本词汇表的生成,【C/C++】词汇表生成
  10. 挑战程序设计竞赛(算法和数据结构)——14.1互质的集合(并查集)的JAVA实现