CMS GC过程

一.什么是CMS收集器
CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动JVM参数加上**-XX:+UseConcMarkSweepGC** ,这个参数表示对于老年代的回收采用CMS。CMS采用的基础算法是:标记—清除

二.CMS工作步骤
在这里插入图片描述

  • 初始标记(STW initial mark)
  • 并发标记(Concurrent marking)
  • 并发预清理(Concurrent precleaning)
  • 重新标记(STW remark)
  • 并发清理(Concurrent sweeping)
  • 并发重置(Concurrent reset)

初始标记 :在这个阶段,需要虚拟机停顿正在执行的任务,官方的叫法STW(Stop The Word)。这个过程从垃圾回收的"根对象"开始,只扫描到能够和"根对象"直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了。

并发标记 :这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。

并发预清理 :并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。可以使用-XX:-CMSPrecleaningEnabled进行关闭,不进行预处理

重新标记 :这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从"跟对象"开始向下追溯,并处理对象关联。(STW)

并发清理 :清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。

并发重置 :这个阶段,重置CMS收集器的数据结构,等待下一次垃圾回收。

三.demo演示
环境:JDK1.8
参数设置:
-Xms10M -Xmx10M -Xmn3m -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

代码:

public class TestCMSGC {
    public static void main(String[] args) {
        byte[] a = new byte[6 * 1024 * 1024];
        a = null;
        byte[] a1 = new byte[5 * 1024 * 1024];
        byte[] a2 = new byte[1 * 1024 * 1024];
        byte[] a3 = new byte[3 * 1024 * 1024];
    }
}

GC日志:

[GC (Allocation Failure) [ParNew: 1545K->256K(2816K), 0.0015253 secs][CMS: 6496K->594K(7168K), 0.0015234 secs] 7689K->594K(9984K), [Metaspace: 3177K->3177K(1056768K)], 0.0031018 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 1103K->168K(2816K), 0.0003180 secs][CMS: 6740K->6734K(7168K), 0.0022887 secs] 6818K->6734K(9984K), [Metaspace: 3192K->3192K(1056768K)], 0.0026347 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [CMS: 6734K->6716K(7168K), 0.0017369 secs] 6734K->6716K(9984K), [Metaspace: 3192K->3192K(1056768K)], 0.0017498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 6716K(7168K)] 6745K(9984K), 0.0001022 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (CMS Final Remark) [YG occupancy: 157 K (2816 K)][Rescan (parallel) , 0.0001984 secs][weak refs processing, 0.0000047 secs][class unloading, 0.0001340 secs][scrub symbol table, 0.0002641 secs][scrub string table, 0.0000800 secs][1 CMS-remark: 6716K(7168K)] 6873K(9984K), 0.0007196 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 2816K, used 157K [0x00000000ff600000, 0x00000000ff900000, 0x00000000ff900000)
  eden space 2560K,   6% used [0x00000000ff600000, 0x00000000ff6274b0, 0x00000000ff880000)
  from space 256K,   0% used [0x00000000ff880000, 0x00000000ff880000, 0x00000000ff8c0000)
  to   space 256K,   0% used [0x00000000ff8c0000, 0x00000000ff8c0000, 0x00000000ff900000)
 concurrent mark-sweep generation total 7168K, used 571K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3237K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

我们先分析minor gc日志
可以看到Allocation Failure,分配失败,进行minor gc,具体参数意义如下
在这里插入图片描述

[GC (Allocation Failure) [ParNew: 1545K->256K(2816K), 0.0015253 secs][CMS: 6496K->594K(7168K), 0.0015234 secs] 7689K->594K(9984K), [Metaspace: 3177K->3177K(1056768K)], 0.0031018 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

然后是CMS GC日志:
可看到日志标黑的部分对应上面的7个阶段
[Full GC (Allocation Failure) [CMS: 6734K->6716K(7168K), 0.0017369 secs] 6734K->6716K(9984K), [Metaspace: 3192K->3192K(1056768K)], 0.0017498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 6716K(7168K)] 6745K(9984K), 0.0001022 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Final Remark) [YG occupancy: 157 K (2816 K)][Rescan (parallel) , 0.0001984 secs][weak refs processing, 0.0000047 secs][class unloading, 0.0001340 secs][scrub symbol table, 0.0002641 secs][scrub string table, 0.0000800 secs][1 CMS-remark: 6716K(7168K)] 6873K(9984K), 0.0007196 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

四.concurrent mode failure & promotion failed
上面的是CMS GC的正常流程,但是回收过程中还可能出现concurrent mode failure
[GC (Allocation Failure) [ParNew: 2110K->2110K(2816K), 0.0000069 secs][CMS (concurrent mode failure): 6329K->5804K(7168K), 0.0031868 secs] 8440K->5804K(9984K), [Metaspace: 3229K->3229K(1056768K)], 0.0032197 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
这表示清理过程中老年代空间不足,因为CMS的垃圾清理和引用线程是并行进行的,如果在并行清理的过程中老年代的空间不足以容纳,从年轻代晋升的新对象,或者直接分配的大对象,则会抛出“concurrent mode failure”。

影响:老年代的垃圾收集器从CMS退化为Serial Old,所有应用线程被暂停,停顿时间变长。
所以如果应用频繁出现这种情况应该进行调整

解决方案
1.增大老年代空间
2.-XX:+UseCMSCompactAtFullCollection 表示CMS在完成后进行空间碎片整理,因为CMS是采用的标记清除算法,所有会产生空间碎片,
-XX:CMSFullGCsBeforeCompaction=n 表示在完成多少次CMS之后,进行空间压缩
3.-XX:CMSInitiatingOccupancyFraction=N调小;这个参数默认是68,表示老年代空间使用率达到68%时进行CMS

promotion failed是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的。

五.CMS重要参数
结合上面的解决方案:
CMS重要参数:
-XX:CMSInitiatingOccupancyFraction:触发CMS收集器的内存比例。比如68%的意思就是说,当内存达到60%,就会开始进行CMS并发收集。
-XX:+UseCMSInitiatingOccupancyOnly 配合上面使用,用于设定回收阈值(上面指定的68%),如果不指定,JVM仅在第一次使用设定值,后续则自动调整。
-XX:+UseCMSCompactAtFullCollection:这个前面已经提过,用于在每一次CMS收集器清理垃圾后送一次内存整理。
-XX:CMSFullGCsBeforeCompaction:设置在几次CMS垃圾收集后,触发一次内存整理。整理碎片会stop-the-world.

参考:
实战Java虚拟机 JVM故障诊断与性能优化
https://blog.csdn.net/muzhixi/article/details/105274542
https://blog.csdn.net/xiaocai9999/article/details/88368395
https://www.cnblogs.com/bootdo/p/10482853.html
https://blog.csdn.net/iamzhongyong/article/details/84512298?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.contro

猜你喜欢

转载自blog.csdn.net/u010857795/article/details/112910417
今日推荐