一、垃圾回收流程
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、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