目录
3、并发预清理(CMS-concurrent-preclean):
4、可中断的并发预清理阶段( CMS-concurrent-abortable-preclean):
一、概述
-
简介:
- CMS收集器是一种以获取最短回收停顿为目标的收集器。
- 由于GC线程在耗时最长的并发标记阶段、并发清除阶段、并发重设阶段都是与用户线程一起工作的,所以从总体上来说,CMS收集器的gc线程是与用户线程并发执行的。
-
触发时机:
-
第一次启动的时机:
- 老年代占用率达到CMSInitiatingOccupancyFraction值(默认为92%)时,jvm会启动第一次CMS GC。
-
之后启动的时机:
- jvm自动判断。
-
参数:
- -XX:+UseCMSInitiatingOccupancyOnly
- 命令JVM不要根据运行时收集的数据来判断什么时候开始gc,而是根据CMSInitiatingOccupancyFraction的值来判断是否进行CMS收集,一般不会打开该开关。
- -XX:CMSInitiatingOccupancyFraction
- 设置触发第一次CMS GC的阈值,老年代占用率达到该值时,jvm会启动第一次CMS GC,默认为92%。
- -XX:+UseCMSInitiatingOccupancyOnly
-
-
收集算法:
- 使用标记-清除算法。
-
GC类型:
- major gc:只收集老年代。
-
收集范围
- 整个老年代。
二、CMS GC过程:
1、初始标记(CMS-initial-mark):
-
标记对象:
- 一个对象被标记,说明这个对象还存活着,不能被收集器回收。
-
哪些对象会被标记:
- 老年代中所有被根对象(GC Roots)直接引用的对象会被标记。
- 老年代中被年轻代中存活对象直接引用的对象会被标记。
-
标记方式:
- 该阶段需要Stop the word,因为仅标记少量节点,所以该阶段带来的暂停时间很短。
-
图示:
2、并发标记(CMS-concurrent-mark):
-
哪些对象会被标记:
- 从初始标记阶段标记的对象出发,沿着引用链往下检索(RootsTracing),标记出引用链上所有老年代中存活的对象。
-
跨代引用对象的标记:
-
标记方式:
- gc线程和应用程序线程是并发执行的,故在并发标记期间可能存在对象的漏标和错标的问题。
-
对象漏标:
- 场景1:在并发标记期间有新的对象进入到老年代(新生代的对象晋升到老年代、在老年代中直接分配对象等),这些新进入老年代的对象没有被标记。
- 场景2:在并发标记期间用户线程修改了存活对象A(已标记)的某个字段:将这个字段指向了一个未被标记过的对象B,那么此时对象B就由(并发标记开始时的)不可达变为可达了,但是对象B却没有被标记。
-
对象误标:
- 某个存活对象(如下图的current obj)的某个字段引用着对象A,在并发标记期间用户线程将该字段修改为引用对象B,那么此时对象A就由(并发标记开始时的)可达变为不可达了,但是对象A却已经被标记过了。
- 某个存活对象(如下图的current obj)的某个字段引用着对象A,在并发标记期间用户线程将该字段修改为引用对象B,那么此时对象A就由(并发标记开始时的)可达变为不可达了,但是对象A却已经被标记过了。
-
对象漏标的解决:
- 在并发标记阶段,一些对象的引用可能已经发生了变化(导致对象漏标或误标),jvm会将这些引用发生变化的对象(包括新进入老年代的对象)所在的Card标记为Dirty Card。
- 在并发标记阶段,一些对象的引用可能已经发生了变化(导致对象漏标或误标),jvm会将这些引用发生变化的对象(包括新进入老年代的对象)所在的Card标记为Dirty Card。
3、并发预清理(CMS-concurrent-preclean):
-
哪些对象会被标记:
-
从Dirty Card中包含的对象开始,沿着引用链往下检索(RootsTracing),标记出引用链上所有老年代中存活的对象。
-
-
标记方式:
- gc线程和应用程序线程并发执行。
- 并发预清理的目的是为了减少重新标记阶段的工作,进而减少STW的时间。
-
Dirty Card恢复成正常的Card:
- 当Dirty Card中所有对象的引用链都检索完成后,这个Card的Dirty标识就被清除了。
- 当Dirty Card中所有对象的引用链都检索完成后,这个Card的Dirty标识就被清除了。
4、可中断的并发预清理阶段( CMS-concurrent-abortable-preclean):
-
触发条件:
- 在并发预清理阶段执行完成之后,如果eden区的占用量大于CMSScheduleRemarkEdenSizeThreshold(默认为2M) ,则会触发本阶段执行。
-
中断条件:
- eden区的占用量大于CMSScheduleRemarkEdenPenetration(默认50%),则中断本阶段。
- 本阶段执行时间大于 CMSMaxAbortablePrecleanTime,则中断本阶段。
-
哪些对象会被标记:
- 和并发预清理阶段标记的对象一样。
-
标记方式:
- gc线程和应用程序线程并发执行,gc线程的标记工作可以被中断。
- 在可中断的并发预清理阶段,jvm期望发生一次minor gc,这样年轻代中无用的对象就被回收掉了,进而可以减少remark阶段扫描对象的数量。
- 并发预清理的目的同样也是为了减少重新标记阶段的工作,进而减少STW的时间。
-
参数:
- -XX:+CMSScheduleRemarkEdenSizeThreshold
- eden区占用量大于该值时(默认为2M),触发可中断的并发预清理阶段启动。
- -XX:CMSScheduleRemarkEdenPenetration
- 表示eden区使用比例超过制定比例就结束该阶段进入remark阶段。
- 默认50%,即:-XX:CMSScheduleRemarkEdenPenetration=50
- -XX:CMSMaxAbortablePrecleanTime
- 如果可中断的预清理执行时间超过该值,那么无论minor gc有没有发生,该阶段都会立即结束,然后进入remark阶段,这样是为了避免因没有等到minor gc而陷入无限等待。
- 默认5秒,即:-XX:CMSMaxAbortablePrecleanTime=5000
- -XX:+CMSScheduleRemarkEdenSizeThreshold
5、重新标记(CMS-remark):
-
哪些对象会被标记:
- 重新标记在并发标记期间遗漏的对象。
- 新生代对象可能持有老年代中对象的引用,故CMS-remark阶段扫描对象的范围是整个堆,故堆(新生代+老年代)中对象的数目影响了Remark阶段耗时。
- 新生代:扫描新生代中所有的区域。
- 老年代:从Dirty Card中包含的对象开始,沿着引用链往下检索(RootsTracing),标记出引用链上所有老年代中存活的对象。
-
标记方式:
- 因为并发预清理是并发执行的,所以对象的引用可能会发生进一步的改变,故jvm在该阶段需要STW,以确保在清理之前保持一个正确的对象引用视图。
- 一般重新标记阶段的暂停时间是比较长的。
-
参数:
- -XX:+CMSScavengeBeforeRemark
- 在remark前强制进行一次minor gc,这样在一定程度上降低了重新标记阶段对“遗漏”对象的扫描时间。
- -XX:+CMSParallelRemarkEnabled
- 可以并行remark,减少暂停的时间
- -XX:+CMSScavengeBeforeRemark
- JVM - GC过程中存活对象的标记_A__17的博客-优快云博客
6、并发清除(CMS-concurrent-sweep):
-
哪些对象会被清除:
- 未被标记的对象。
-
清除方式:
- gc线程和应用程序线程并发执行。
- 使用标记-清除法回收清除老年代的垃圾对象(未被标记的对象)
-
浮动垃圾:
- 概念:在并发清除阶段产生的垃圾称为浮动垃圾。
- 说明:浮动垃圾在本次gc中是无法清除的,只能等到下次清理。
7、并发重设(CMS-concurrent-reset):
-
工作内容:
- 对记录标记对象的表等数据结构做重置处理,为下一次GC做准备。
-
工作方式:
- 并发重设线程和应用程序线程是并发执行的。
三、CMS GC失败后的预案:
- 如果CMS运行期间预留的内存无法满足程序需要,那么就会出现一次“Concurrent Mode Failure”失败,此时JVM将启动后备预案:
- 使用Serial Old收集器重新对老年代进行gc,这样一来,停顿时间就会很长。
四、CMS GC的两种模式:
-
background模式:
- 触发条件:old的内存占比超过多少的时候就可能触发一次background式的cms gc
- 其中background顾名思义是在后台做的,也就是可以不影响正常的业务线程跑,这个过程会经历CMS GC的所有阶段,该暂停的暂停,该并行的并行,效率相对来说还比较高,毕竟有和业务线程并行的gc阶段;
-
foreground模式:
- 触发条件:比如业务线程请求分配内存,但是内存不够了,于是可能触发一次cms gc
- 这个过程就必须是要等内存分配到了线程才能继续往下面走的,因此整个过程必须是STW的,因此CMS GC整个过程都是暂停应用的,但是为了提高效率,它并不是每个阶段都会走的,只走其中一些阶段,这些省下来的阶段主要是并行阶段,Precleaning、AbortablePreclean,Resizing这几个阶段都不会经历,其中sweep阶段是同步的,但不管怎么说如果走了类似foreground的cms gc,那么整个过程业务线程都是不可用的,效率会影响挺大。
- 参考:JVM源码分析之SystemGC完全解读 - 你假笨