当前位置: 首页 > 工具软件 > Current CMS > 使用案例 >

CMS垃圾回收器

李耀
2023-12-01

一、垃圾回收流程

1、初始标记(STW)

    1.1、标记老年代中所有的GC Roots对象

    1.2、标记老年代中被年轻代中活着的对象引用的对象

2、并发标记

    2.1、从初始标记收集到的"根"对象引用开始,遍历出所有被引用的对象

    PS:因为是并发运行的,在运行期间会发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代; 
并发标记阶段只负责将引用发生改变的Card标记为Dirty状态,不负责处理;
 

3、并发预清理与可中断并发预清理

     标记在并发标记阶段引用发生变化的对象

    其中可中断并发预清理阶段最大持续时间为5秒,之所以可以持续5秒,另外一个原因也是为了期待这5秒内能够发生一次ygc,清理年轻带的引用,是的下个阶段的重新标记阶段,扫描年轻带指向老年代的引用的时间减少

    PS:该两阶段阶段做的工作还是标记,与4的重标记功能相似,主要是为了降低重标记的工作以减少STW的时间

    

4、重新标记(STW)

     由于并发预处理是并发的,对象引用可能发生进一步变化。因此,应用程序线程会再一次被暂停以更新这些变化

5、并发清理

    并发清理垃圾数据

6、并发重置

     CMS清除内部状态,为下次回收做准备。

二、一次CMS的日志

[GC (CMS Initial Mark) [1 CMS-initial-mark: 1165388K(1916928K)] 1262805K(3022848K), 0.0165822 secs] [Times: user=0.07 sys=0.00, real=0.02 secs] (初始标记)
[CMS-concurrent-mark-start]   (并发标记)
[CMS-concurrent-mark: 0.358/0.371 secs] [Times: user=1.13 sys=0.05, real=0.37 secs] 
[CMS-concurrent-preclean-start]  (预清理)
[CMS-concurrent-preclean: 0.008/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[CMS-concurrent-abortable-preclean-start] (可中断预清理)
[GC (Allocation Failure) 3099.441: [ParNew: 1074360K->122880K(1105920K), 0.0442994 secs] 2239748K->1289009K(3022848K), 0.0445459 secs] [Times: user=0.27 sys=0.00, real=0.04 secs]  (中断期间执行了一次yong GC)
 CMS: abort preclean due to time 2019-06-19T15:18:45.411+0800: 3101.771: [CMS-concurrent-abortable-preclean: 5.406/5.461 secs] [Times: user=8.50 sys=0.79, real=5.46 secs] 
[GC (CMS Final Remark) [YG occupancy: 485381 K (1105920 K)]3101.773: [Rescan (parallel) , 0.0497189 secs]3101.822: [weak refs processing, 0.0020910 secs]3101.825: [class unloading, 0.0895745 secs]3101.914: [scrub symbol table, 0.0108606 secs]3101.925: [scrub string table, 0.0022586 secs][1 CMS-remark: 1166129K(1916928K)] 1651511K(3022848K),0.2080657 secs] [Times: user=0.51 sys=0.02, real=0.21 secs]   (重新标记)
[CMS-concurrent-sweep-start]  (并发清理)
[CMS-concurrent-sweep: 0.720/0.720 secs] [Times: user=1.06 sys=0.11, real=0.72 secs] 
[CMS-concurrent-reset-start] (并发重置)
[CMS-concurrent-reset: 0.005/0.005 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 

三、CMS优缺点

1、缺点

  1、CPU资源敏感

   2、产生浮动垃圾

  3、产生内存碎片

特点:

  • cms只会回收老年代和永久带(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻带;
  • cms是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%;

四、CMS可能遇到的问题

1、promotion failed

在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的,多数是由于老年带有足够的空闲空间,但是由于碎片较多,新生代要转移到老年带的对象比较大,找不到一段连续区域存放这个对象导致的

2、concurrent mode failure

这个异常发生在cms正在回收的时候。执行CMS GC的过程中,同时业务线程也在运行,当年轻带空间满了,执行ygc时,需要将存活的对象放入到老年代,而此时老年代空间不足,这时CMS还没有机会回收老年带产生的,或者在做Minor GC的时候,新生代救助空间放不下,需要放入老年代,而老年代也放不下而产生的

PS:通过是promotion failed发生,引起了oncurrent mode failure,以下是从别出复制的一段日志

106.641: [GC 106.641: [ParNew (promotion failed): 14784K->14784K(14784K), 0.0370328 secs]106.678: [CMS106.715: [CMS-concurrent-mark: 0.065/0.103 secs] [Times: user=0.17 sys=0.00, real=0.11 secs] 
(concurrent mode failure): 41568K->27787K(49152K), 0.2128504 secs] 52402K->27787K(63936K), [CMS Perm : 2086K->2086K(12288K)], 0.2499776 secs] [Times: user=0.28 sys=0.00, real=0.25 secs]
 

解决方法:

-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70

提前触发CMS

五、CMS不是Full GC

六、其他导致CMS GC慢的原因

如果是IO引起GC慢

user是用户线程占用的时间,sys是系统线程占用的时间,real实际时间 
1. user与sys时间都非常小,但是real却很长

[ Times: user=0.51 sys=0.10, real=5.00 secs ] 

可能是IO引起的

2、sys时间很长,user时间很短,real几乎等于sys的时间,如下:
[ Times: user=0.11 sys=31.10, real=33.12 secs ] 

可能是大内存进行swap交换时会有这种现象

七、参考文献:

https://blog.csdn.net/zqz_zqz/article/details/70568819 

https://www.jianshu.com/p/08f0b85ad665

 类似资料: