Java8的CMS垃圾回收器官方文档参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector

一、CMS简介

并发标记清除(Concurrent Mark Sweep, CMS)收集器是为那些喜欢更短的垃圾收集暂停时间,并且能够在应用程序运行时与垃圾收集器共享处理器资源的应用程序设计的
通常,具有相对较大的长寿命数据集(大的年老代)并运行在具有两个或更多处理器的机器上的应用程序往往会受益于使用这种收集器。但是,对于任何暂停时间要求较低的应用程序,都应该考虑使用此收集器。
CMS收集器通过命令行选项启用-XX:+UseConcMarkSweepGC

与其他可用收集器类似,CMS收集器是分代的;因此,minor collection和major collection同时发生。CMS收集器试图通过使用单独的垃圾收集器线程在执行应用程序线程的同时跟踪可到达的对象,从而减少由于主要收集而导致的暂停时间。在每个主要的收集周期中,CMS收集器在收集开始时暂停所有应用程序线程一小段时间,然后在收集中期再次暂停。第二个停顿往往是两个停顿中较长的一个。在两次暂停期间使用多个线程执行收集工作。收集的其余部分(包括活动对象的大部分跟踪和不可到达对象的清除)是通过一个或多个与应用程序同时运行的垃圾收集器线程完成的。次要收集可以与正在进行的主循环交织,并且以类似于并行收集器的方式完成(特别是,在次要收集期间停止应用程序线程)。

二、CMS两种模式,一种压缩策略,一种OOM机制

  1. Backgroud CMS (正常进行的CMS)

    (1)初始标记 CMS initial mark     标记GC Roots直接相关联的第一个对象,不用Tracing,速度很快 STW (jdk7默认单线程,jdk8默认多线程-XX:+CMSParallelInitialMarkEnabled=true)
    (2)并发标记 CMS concurrent mark  进行GC Roots Tracing,找出初始标记对象的所有的引用链上的剩余对象 (多线程并发执行,GC线程和应用的业务线程同时存在)
    (3)重新标记 CMS remark           修改并发标记因用户程序变动的内容 (前期多线程末尾执行STW)
    (4)并发清除 CMS concurrent sweep 清除不可达对象回收空间,同时有新垃圾产生,留着下次清理称为浮动垃圾 (多线程并发执行,GC线程和应用的业务线程同时存在)
    

  2. Foregroud CMS (并发模式失败的CMS)(见第三部分)

  3. CMS的标记压缩算法MSC(Mark Sweep Compact)

    // 开启在FULL GC 之后进行内存压缩,MSC使用的时Lisp2滑动整理算法
    -XX:+UseCMSCompactAtFullCollection (java8 默认开启,java 11没有此配置)
    // 0表示每次Full GC 之后都会压缩  (可以配置高一点,比如:5)
    -XX:CMSFullGCsBeforeCompaction=0(java8 默认0,java 11没有此配置)
    
  4. OOM机制(见第四部分)

三、并发模式失败 Concurrent Mode Failure

CMS收集器使用一个或多个垃圾收集器线程,这些线程与应用程序线程同时运行,目标是在年老代满之前完成年老代的收集。如前所述,在正常操作中,CMS收集器在应用程序线程仍然运行的情况下执行大部分跟踪和清理工作,因此应用程序线程只能看到短暂的暂停。但是,如果CMS收集器无法在年老代填满之前完成回收不可到达的对象,或者如果年老代中的可用空闲空间块不能满足分配,那么应用程序将暂停,并在停止所有应用程序线程的情况下完成收集(STW)。不能同时完成收集被称为并发模式失败,这表明需要调整CMS收集器参数。如果并发收集被显式垃圾收集(System.gc())或为诊断工具提供信息所需的垃圾收集中断,则报告并发模式中断。

调整CMS收集器参数:

// 开启时,只有当老年代的内存使用达到设置的百分比时才会触发CMS垃圾回收
-XX:CMSInitiatingOccupancyFraction  (默认-1不启用,默认使用CMSTriggerRatio等参数计算比例)
// 只使用CMSInitiatingOccupancyFraction参数设定的回收阈值
-XX:+UseCMSInitiatingOccupancyOnly  (默认false)

默认的老年代触发CMS垃圾回收的百分比计算公式:

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


Java8的CMSTriggerRatio 默认80,MinHeapFreeRatio默认值40

((100 - 40) + (double)( 80 * 40) / 100.0) / 100.0 = 0.92

占比92%(比较容易出现并发模式失败),需要调整参数。

// 比如设置占比为70%触发CMS
-XX:CMSInitiatingOccupancyFraction=70,去控制当老年代的内存占用达到70%的时候,便开启CMS回收年老代。

四、GC超时和内存溢出 Excessive GC Time and OutOfMemoryError

如果在垃圾收集上花费的时间太多,CMS收集器就会抛出一个OutOfMemoryError:如果花费在垃圾收集上的总时间超过98%,而回收堆的时间少于2%,那么就会抛出一个OutOfMemoryError。 此特性旨在防止应用程序运行一段较长的时间,而由于堆太小而几乎没有任何进展。如果需要,可以通过在命令行中添加选项-XX:-UseGCOverheadLimit来禁用该特性(默认开启)

该策略与并行收集器中的策略相同,只是执行并发收集的时间不计入98%的时间限制。换句话说,只有在应用程序停止时执行的收集才会导致过多的GC时间。这样的集合通常是由于并发模式失败或显式的垃圾回收请求(例如,对System.gc的调用)造成的

五、浮动垃圾 Floating Garbage

CMS收集器与Java HotSpot VM中的所有其他收集器一样,是一个跟踪收集器,它至少标识堆中所有可到达的对象。用Richard Jones和Rafael D. Lins在他们发表的《垃圾收集:自动动态内存算法》中的话说,它是一个增量更新收集器(incremental update collector)由于应用程序线程和垃圾收集器线程在主要收集期间并发运行,垃圾收集器线程跟踪的对象可能在收集过程结束时变得不可访问。这种尚未被回收的不可达对象称为浮动垃圾

漏标会导致被引用的对象被当成垃圾误删除,增量更新是解决漏标问题的一种方式,G1使用原始快照(Snapshot At The Beginning,SATB)的方式处理

浮动垃圾的数量取决于并发收集周期的持续时间和应用程序的引用更新(也称为突变)的频率。此外,由于年轻代和年老代是独立收集的,每一个都是另一个的根的来源。作为一个粗略的指导方针,尝试增加20%的年老代的大小,以占浮动垃圾的比例。在一个并发收集周期结束时,将在下一个收集周期期间收集堆中的浮动垃圾

六、停顿 Pauses

CMS收集器在并发收集周期中暂停应用程序两次。
第一个暂停是将从根(例如,来自应用程序线程堆栈和寄存器的对象引用、静态对象等)和从堆的其他地方(例如,年轻代)直接可访问的对象标记为活动对象

第一次暂停称为初始标记停顿。第二次暂停出现在并发跟踪阶段的末尾,查找由于CMS收集器完成跟踪对象后应用程序线程更新对象中的引用而导致并发跟踪错过的对象。这第二次停顿称为重新标记停顿

七、并发阶段 Concurrent Phases

可达对象图的并发跟踪发生在初始标记停顿和重新标记停顿之间。在此并发跟踪阶段,一个或多个并发垃圾收集器线程可能正在使用应用程序本来可用的处理器资源。因此,即使应用程序线程没有暂停,计算绑定的应用程序在此和其他并发阶段的应用程序吞吐量也可能会出现相应的下降。在重新标记停顿之后,一个并发清除阶段将收集标识为不可达的对象。一旦一个收集周期完成,CMS收集器就会等待,几乎不消耗任何计算资源,直到下一个主要收集周期开始。

八、开始并发收集周期 Starting a Concurrent Collection Cycle

对于串行收集器,只要年老代已满,并且在收集完成时停止所有应用程序线程,就会发生主收集。相反,并发收集的开始必须定时,以便收集可以在年老代满之前完成;否则,由于并发模式失败,应用程序将观察到更长的暂停。有几种方法可以启动并发收集

CMS收集器根据最近的历史记录,维护年老代耗尽之前剩余时间的估计,以及并发收集周期所需的时间。使用这些动态估计,开始一个并发收集周期,目的是在年老代耗尽之前完成收集周期。这些估计是为了安全而进行的,因为并发模式故障的代价可能非常高。

如果年老代的占用率超过初始占用率(年老代的百分比),也会启动并发收集。这个初始占用阈值的默认值大约是92%(参考第三部分),但是该值会随着版本的不同而变化。该值可以使用命令行选项手动调整-XX: cmsinitiatingocupancyfraction =,其中是年老代大小的整数百分比(0到100)。

九、调度停顿 Scheduling Pauses

年轻代收集和年老代收集的停顿是独立发生的。它们不重叠,但可以快速连续地出现,这样一个集合的停顿,紧接着是另一个集合的停顿,看起来可能是一个单独的、更长的停顿。
为了避免这种情况,CMS收集器尝试在上一代和下一代年轻代停顿之间安排重新标记停顿。目前还没有对初始标记停顿执行此调度,初始标记停顿通常比重新标记停顿短得多

十、增量模式 Incremental Mode

请注意,增量模式在Java SE 8中已被弃用,并可能在未来的主要版本中被删除。

CMS收集器可以在并发阶段逐步完成的模式中使用。回想一下,在并发阶段,垃圾收集器线程正在使用一个或多个处理器。增量模式旨在通过定期停止并发阶段,将处理器交还给应用程序,从而减少长并发阶段的影响。这种模式在这里称为i-cms,它将收集器并发完成的工作划分为在年轻代收集之间调度的小时间块。当需要CMS收集器提供的低停顿时间的应用程序运行在具有少量处理器(例如,1或2)的机器上时,此特性非常有用

并发收集周期通常包括以下步骤:

  • 停止所有应用程序线程,标识从根可达的对象集,然后恢复所有应用程序线程。(STW 初始标记)

  • 在应用程序线程执行时,使用一个或多个处理器并发跟踪可达的对象图。(并发标记)

  • 使用一个处理器,同时回溯自上一步跟踪以来被修改的对象图的部分。(重新标记)

  • 停止所有应用程序线程,并回溯根和对象图中自上次检查以来可能被修改的部分,然后恢复所有应用程序线程。(STW重新标记的末尾停顿,确定本次需要清除的垃圾对象)

  • 使用一个处理器,将不可达的对象并发清除到用于分配的空闲列表中。(并发清除)

  • 使用一个处理器并发地调整堆的大小,并为下一个收集周期准备支持数据结构。

通常,CMS收集器在整个并发跟踪阶段使用一个或多个处理器,而不会主动放弃它们。类似地,一个处理器用于整个并发清除阶段,同样不放弃它。对于响应时间受限的应用程序来说,这种开销可能会造成很大的干扰,这些应用程序可能会使用处理核心,特别是在只有一个或两个处理器的系统上运行时。增量模式通过将并发阶段分解为活动的短爆发来解决这个问题,这些活动被安排在小暂停之间的中途发生

i-cms模式使用工作周期来控制CMS收集器在自愿放弃处理器之前所允许做的工作量。工作周期是允许CMS收集器运行的年轻代收集之间的时间百分比。i-cms模式可以根据应用程序的行为自动计算工作周期(推荐的方法,称为自动步调),也可以在命令行中将工作周期设置为一个固定值。

十一、命令行选项 Command-Line Options

表8-1“命令行选项for i-cms”列出了控制i-cms模式的命令行选项。推荐选项一节建议了一组初始选项。
表8-1 i-cms命令行选项:

推荐选项:
要在Java SE 8中使用i-cms,请使用以下命令行选项:

-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps

前两个选项分别启用CMS收集器和i-cms。最后两个选项不是必需的;它们只是导致将有关垃圾收集的诊断信息写入标准输出,以便可以看到垃圾收集行为并在以后进行分析。

对于Java SE 5和更早的版本,Oracle建议使用以下作为i-cms的初始命令行选项集:

-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps \
-XX:+CMSIncrementalPacing -XX:CMSIncrementalDutyCycleMin=0
-XX:CMSIncrementalDutyCycle=10

JavaSE8也推荐使用相同的值,不过JavaSE6中控制i-cms自动调速的三个选项的值是默认值。

十二、基本故障排除 Basic Troubleshooting

i-cms自动调整特性使用程序运行时收集的统计信息来计算工作周期,以便在堆被填满之前完成并发收集。然而,过去的行为并不能完美地预测未来的行为,而且估计可能并不总是足够准确,以防止堆被填满。如果出现太多的全收集,请尝试表8-2“i-cms自动步调特性故障排除”中的步骤,一次一个。
表8-2 i-cms自动步调故障处理

十三、测量指标 Measurements

备注:次要收集(minor collection) 通常指的是年轻代垃圾收集remark 指重新标记

示例8-1,“CMS收集器的输出”是CMS收集器的输出,带有选项-verbose:gc和-XX:+PrintGCDetails,删除了一些次要细节。注意,CMS收集器的输出与次要集合的输出穿插在一起;
通常,在并发收集周期中会发生许多次要收集(minor collections)。CMS-initial-mark表示并发收集周期的开始,CMS-concurrent-mark表示并发标记阶段的结束,CMS-concurrent-sweep表示并发扫描阶段的结束。
前面没有讨论由CMS-concurrent-preclean表示的预清洗阶段。预清理代表可以在准备重新标记阶段CMS-remark时同时进行的工作。最后一个阶段由CMS-concurrent-reset指示,并为下一次并发收集做准备
示例8-1 CMS Collector的输出信息

// CMS-initial-mark
[GC [1 CMS-initial-mark: 13991K(20288K)] 14103K(22400K), 0.0023781 secs]
[GC [DefNew: 2112K->64K(2112K), 0.0837052 secs] 16103K->15476K(22400K), 0.0838519 secs]
...
[GC [DefNew: 2077K->63K(2112K), 0.0126205 secs] 17552K->15855K(22400K), 0.0127482 secs]
// CMS-concurrent-mark
[CMS-concurrent-mark: 0.267/0.374 secs]
[GC [DefNew: 2111K->64K(2112K), 0.0190851 secs] 17903K->16154K(22400K), 0.0191903 secs]
// CMS-concurrent-preclean
// -XX:+CMSPrecleaningEnabled(默认开启)
[CMS-concurrent-preclean: 0.044/0.064 secs]
[GC [1 CMS-remark: 16090K(20288K)] 17242K(22400K), 0.0210460 secs]
[GC [DefNew: 2112K->63K(2112K), 0.0716116 secs] 18177K->17382K(22400K), 0.0718204 secs]
[GC [DefNew: 2111K->63K(2112K), 0.0830392 secs] 19363K->18757K(22400K), 0.0832943 secs]
...
[GC [DefNew: 2111K->0K(2112K), 0.0035190 secs] 17527K->15479K(22400K), 0.0036052 secs]
// CMS-concurrent-sweep
[CMS-concurrent-sweep: 0.291/0.662 secs]
[GC [DefNew: 2048K->0K(2112K), 0.0013347 secs] 17527K->15479K(27912K), 0.0014231 secs]
// CMS-concurrent-reset
[CMS-concurrent-reset: 0.016/0.016 secs]
[GC [DefNew: 2048K->1K(2112K), 0.0013936 secs] 17527K->15479K(27912K), 0.0014814 secs
]

相对于次要收集停顿时间,初始标记停顿通常较短。并发阶段(并发标记、并发预清理和并发扫描)通常比minor collection停顿持续的时间长得多,如示例8-1所示,“CMS收集器的输出”。但是请注意,在这些并发阶段中应用程序没有停顿。重新标记停顿的长度通常与minor collection的长度相当。重新标记停顿受某些应用程序特征的影响(例如,对象修改的高比例可能会增加此停顿),以及自上次次要收集(minor collection)以来的时间(例如,年轻代中更多的对象可能会增加此停顿)。

十四、并发预清理 CMS-concurrent-preclean

CMS-concurrent-preclean并发预清理配置:

如何判断老年代中的对象有没有被年轻代中的对象引用?(如何判断老年代中的对象是否存活),通过扫描年轻代来确定。但是扫面年轻代,垃圾回收的停顿时间就会变成,所以需要在扫面之前,进行一次young GC(minor collection),回收之后,对象变少了,扫描就快了,但是触发预清理是有条件的:

-XX:CMSScheduleRemarkEdenSizeThreshold 默认值:2M
-XX:CMSScheduleRemarkEdenPenetration 默认值:50%

Eden空间使用超过2M的时候启动可中断的并发预清理(CMS-concurrent-abortable-preclean),到Eden空间使用率到达50%的时候中断(但不是结束),进入Remark(重新标记阶段)

CMS-concurrent-abortable-preclean可中断的并发预清理并不是一直执行的,是有时间限制的,当超过一定的时间(CMSMaxAbortablePrecleanTime,默认5s)即使Eden空间使用率没有到达50%,一样会停止收集,进入remark阶段。

// 默认不循环
-XX:CMSMaxAbortablePrecleanLoops=0
// 可中断的并发预清理最大执行时间
-XX:CMSMaxAbortablePrecleanTime=5000

如果在5s内,minor GC没有执行,也可以通过CMSScavengeBeforeRemark参数配置,在重新标记之前,强制进行一次Minor GC。

// 默认false,开启使用-XX:+CMSScavengeBeforeRemark
-XX:-CMSScavengeBeforeRemark

JVM垃圾回收器-CMS并发标记清除相关推荐

  1. jvm垃圾回收策略之标记清除

    垃圾回收指的是对 jvm堆内存的回收. 一. java虚拟机栈 二.本地方法栈(Native Method Stack) 本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点以及都能抛出Stac ...

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

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

  3. JVM垃圾回收器cms详解

    关于serial,parnew,parallel等回收器的介绍可以参考上一篇jvm垃圾回收算法以及垃圾回收器,如何选择_bjzw的博客-CSDN博客 下面具体介绍一下cms,jdk8之后就已经废弃了c ...

  4. jvm 垃圾回收器的并发与并行

    并行:多个垃圾回收线程同时进行 并发:用户线程和垃圾回收线程同时进行 这里的并发与并行的概念需要与多线程中的概念加以区分,详情可以参考这边博文:多线程 并行与并发的区别

  5. java cms 垃圾回收_Java 9 或将放弃 CMS(并发标记清除垃圾收集器)

    原标题:Java 9 或将放弃 CMS(并发标记清除垃圾收集器) 近日,JEP 291 再次被 Java 开发团队提上日程.先来看看该 JEP. JEP 291: Deprecate the Conc ...

  6. 垃圾回收只知道标记清除?一文帮你打通V8垃圾回收

    前言 本文旨在帮你建议起垃圾回收的体系化知识,仔细品味目录,相信你能体会得的到. V8 垃圾回收核心思路 第一步 通过 GC Root 标记活动对象与非活动对象 如何判断活动对象? V8 通过 可访问 ...

  7. JVM系列(十一) 垃圾收集器之 Concurrent Mark Sweep 并发标记清除

    垃圾收集器之 Concurrent Mark Sweep 并发标记清除 上几篇文章我们讲解了单线程垃圾收集器 Serial/SerialOld ,多线程垃圾收集器 Parallel Scavenge/ ...

  8. Jvm垃圾回收器(终结篇)

    知识回顾: 第一篇<Jvm垃圾回收器(基础篇)>主要讲述了判断对象的生死?两种基础判断对象生死的算法.引用计数法.可达性分析算法,方法区的回收.在第二篇<Jvm垃圾回收器(算法篇)& ...

  9. JVM 垃圾回收器工作原理及使用实例介绍

    2019独角兽企业重金招聘Python工程师标准>>> 垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自 ...

最新文章

  1. 数据归一化 - MinMaxScaler()/MaxAbsScaler() - Python代码
  2. Acwing291. 蒙德里安的梦想:状态压缩dp
  3. 十大排序算法之快速排序(两种方法)
  4. 【LiveVideoStack线上分享】— FreeSWITCH核心架构与流程控制
  5. MS12_020漏洞
  6. 2个css特效冲突了怎么办_患上类风湿病怎么办?2个方法拿走不谢
  7. 基于STM32的高精度频率计设计
  8. python性能解决的事_Python程序的性能分析方法
  9. 排序算法之二 插入排序(C++版本)
  10. Word 2010—样式集
  11. 计算机一级题库ps视频,计算机一级Photoshop题库及答案
  12. Halcon教程十一:小球识别,初识腐蚀与膨胀,开运算和闭运算
  13. 这五款特别的蓝牙芯,哪款最合你应用
  14. 鼠标在用了一段之后偶然发现以给特别慢的速度移动鼠标,会在移动到可点击的选项按钮附近明显的卡住问题。
  15. 【DPABI教程】DPARSF详细教程笔记
  16. 阿里云ECS服务器Linux第一次登录 提示Login Incorrect的解决方法
  17. 征途2经典版服务器双线哪个稳定,征途,别毁了自己和曾经的经典
  18. mysql8 中的rank_Mysql8.0+中的rank()、row_num()、dense_rank()等窗口函数
  19. 数据治理体系解决方案(附PDF全文)
  20. 【读过的书】《查令十字街84号》

热门文章

  1. gitlab本地配置_Gitlab的安装及基本使用(一)
  2. python输入素数为什么要先判断是否是素数再用欧拉筛法
  3. 海康威视RTSP取流
  4. 复数乘法(JAVA)
  5. 小米手机安装推特后频繁闪退
  6. CEO年薪过亿,谁说了算?
  7. 【luogu P3426】SZA-Template(字符串)(KMP)
  8. 新松机器人BG总裁高峰_新松机器人:做一个有价值的机器人企业!
  9. 远程电脑打游戏鼠标漂移无法操作解决办法
  10. 使用VSLinux插件开发和调试