CMS

一种以获取最短回收停顿时间为目标的收集器。
特点:基于标记-清除算法实现。并发收集、低停顿。
应用场景:适用于注重服务的响应速度,希望系统停顿时间STW最短,给用户带来更好的体验等场景下。如web程序、b/s服务。
CMS收集器的缺点:
  • CMS收集器对CPU资源非常敏感,在并发阶段虽然不会导致用户线程停顿,但是会因为占用了一部分CPU资源,如果在CPU资源不足的情况下应用会有明显的卡顿。
  • 无法处理浮动垃圾:在执行并发清除步骤时,用户线程也会同时产生一部分可回收对象,但是这部分可回收对象只能在下次执行清理是才会被回收。如果在清理过程中预留给用户线程的内存不足就会出现Concurrent Mode Failure,一旦出现此错误时便会切换到SerialOld收集方式。
  • CMS清理后会产生大量的内存碎片,当有不足以提供整块连续的空间给新对象/晋升为老年代对象时又会触发FullGC,在JDK1.9后将其废除。

回收日志

2021-08-21T22:43:22.587+0800: 13.877: [GC (CMS Initial Mark) [1 CMS-initial-mark: 104342K(4023936K)] 121165K(4177280K), 
0.0083045 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
2021-08-21T22:43:22.596+0800: 13.886: [CMS-concurrent-mark-start]
2021-08-21T22:43:22.670+0800: 13.960: [CMS-concurrent-mark: 0.073/0.074 secs] [Times: user=0.15 sys=0.00, real=0.08 secs] 
2021-08-21T22:43:22.670+0800: 13.960: [CMS-concurrent-preclean-start]
2021-08-21T22:43:22.677+0800: 13.967: [CMS-concurrent-preclean: 0.006/0.006 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2021-08-21T22:43:22.677+0800: 13.967: [CMS-concurrent-abortable-preclean-start]
2021-08-21T22:43:23.349+0800: 14.639: [CMS-concurrent-abortable-preclean: 0.489/0.672 secs] [Times: user=0.94 sys=0.02, real=0.67 secs] 
2021-08-21T22:43:23.354+0800: 14.644: [GC (CMS Final Remark) [YG occupancy: 88887 K (153344 K)]14.644: [Rescan (parallel) , 
0.0273294 secs]14.671: [weak refs processing, 0.0003408 secs]14.671: [class unloading, 0.0091931 secs]14.681: [scrub symbol table, 
0.0064671 secs]14.687: [scrub string table, 0.0009541 secs][1 CMS-remark: 104342K(4023936K)] 193229K(4177280K), 0.0455322 secs] 
[Times: user=0.07 sys=0.00, real=0.04 secs] 
2021-08-21T22:43:23.399+0800: 14.690: [CMS-concurrent-sweep-start]
2021-08-21T22:43:23.435+0800: 14.725: [CMS-concurrent-sweep: 0.035/0.035 secs] [Times: user=0.06 sys=0.00, real=0.04 secs] 
2021-08-21T22:43:23.435+0800: 14.725: [CMS-concurrent-reset-start]
2021-08-21T22:43:23.444+0800: 14.734: [CMS-concurrent-reset: 0.009/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 

回收流程

初始标记(CMS Initial Mark)

发生STW,该阶段进行可达性分析,标记GC ROOT能直接关联到的对象,所以很快。

并发标记(CMS-concurrent-mark)

由前阶段标记过的对象出发,所有可到达的对象都在本阶段中标记。

并发预清理(CMS-concurrent-preclean)

card marking 产生的 dirty card 进行 clean,cms gc 线程会扫描 dirty card 对应的内存区域,更新之前记录的过时的引用信息,并且去掉 dirty card 的标记。

 

 清洗卡片

并发可中断的预清理(CMS-concurrent-abortable-preclean)

减轻 final remark 阶段(STW会暂停应用线程)的负担,这个阶段同样会对 dirty card 的扫描/清理,和 concurrent-preclean 的区别在于,concurrent-abortable-preclean 会重复地以迭代的方式执行,直到满足退出条件。concurrent-preclean 已经处理过 dirty card,为什么 jvm 还需要再执行一个类似的阶段呢?如果 final-remark 阶段开始时刚好进行了 young gc(比如 ParNew),应用程序刚因为 young gc 暂停,然后又会因为 final-remark 暂停,造成连续的长暂停。除此之外,因为 young gc 线程修改了存活对象的引用地址,会产生很多需要重新扫描的对象,增加了 final-remark 的工作量。 所以 concurrent-abortable-preclean 除了 clean card 的作用,还有调度 final-remark 开始时机的作用参考[2]。cms 回收器认为,final-remark 最理想的执行时机就是年轻代占用在 50%时,这时刚好处于上次 young gc 完成(0%)和下次 young gc 开始(100%)的中间节点,如图所示:

abortable-preclean 的中断条件,配置参数-XX:CMSScheduleRemarkEdenPenetration=50(默认值),表示当 eden 区内存占用到达 50%时,中断 abortable-preclean,开始执行 final-remark。
abortable-preclean 的触发条件,配置参数 -XX:CMSScheduleRemarkEdenSizeThreshold=2m(默认值),表示当 eden 内存占用超过 2mb 时才会执行 abortable-preclean,否则没有执行的必要。
abortable-preclean 的主动退出条件,配置参数-XX:CMSMaxAbortablePrecleanTime=5000和 CMSMaxAbortablePrecleanLoops,主要因为如果年轻代内存占用增长缓慢,那么 abortable-preclean 要长时间执行,可能因为 preclean 赶不上应用线程创造 dirty card 的速度导致 dirty card 越来越多,此时还不如执行一个 final-remark。

重新标记(CMS Final Remark)

发生STW,暂停所有用户线程,重新扫描堆中(新生代+老年代)的对象,进行可达性分析,标记活着的对象。

  • Rescan (parallel) :重新扫描
  • weak refs processing:处理弱引用
  • class unloading:类卸载。可以通过-XX:-CMSClassUnloadingEnabled关闭。
  • scrub symbol table:清理元数据符号引用
  • scrub string table:清理类型表
  • CMS-remark:

并发清除(CMS-concurrent-sweep)

标记清除算法进行并发清除,会产生内存碎片

posted on 2023-03-19 18:46  zhengbiyu  阅读(71)  评论(0编辑  收藏  举报