Concurrent Mark and Sweep

Concurrent Mark and Sweep

优点:大量的工作听过并发线程处理,不需要stop-the-world

缺点:老年代碎片多且在某些情况下暂停时间不可预测,特别是大型堆栈。

启用参数:

  -XX:+UseConcMarkSweepGC

主要目的是:

      减少GC时间,因为要抢占多大多数的cpu时间或全部cpu时间,所以应用的吞吐量相对于在Parallel GC会小。

并发标记清除算法:在Young Generation使用的是并行mark-copy且stop-the-world算法;在Old Generation使用的是大多数的并发mark-sweep算法。为了防止长时间的暂停在Old Generation:第一、不压缩而是使用free-list管理空闲空间;第二、并发执行mark-sweep,不会stop-the-world,但也是会与应用竞争CPU时间;

默认执行并发垃圾收集的线程数为 ( 并行垃圾收集线程数 + 3 )/ 4

并发线程数量

-XX:+ConcGCThreads=

启用并发标记清除算法

-XX:+UseConcMarkSweepGC

查看线程的核数命令:

java -XX:+PrintFlagsFinal -version | grep ParallelGCThreads

1:GC触发时间戳
2:GC触发时间,相对于JVM启动的时间,单位是秒
3:GC区分标志,区分是Minor GC和Full GC,这里是Minor GC
4:GC触发原因,这里是Young Generation空间不足
5:区分使用的收集器,ParNew表示在Young Generation使用的是mark-copy且stop-the-world的收集器,与Old Generation中的Concurrent Mark & Sweep 收集器一同工作
6:Young Generation在GC前后的对象占用空间大小
7:Young Generation总的空间大小
8:没有最终清理垃圾对象的GC持续时间
9:堆收集前后对象数据占用的空间大小
10:堆的总的空间大小
11:在Young Generation执行mark-copy收集器的持续时间,单位为秒,包括与ConcurrentMarkSweep通信的时间、足够老的对象提升到老年代的时间、一些最终清理的时间。
12:-user:GC占用CPU时间
        -sys:GC系统调用的时间或等待系统事件的时间
        -real:应用挂起时间,实际值会超出(user time + sys time)/垃圾收集使用到的线程数,会超出是因为有些操作是非并行化的。
 

Full GC

2015-05-26T16:23:07.321-0200: 64.425: [GC (CMS Initial Mark) [1 CMS-initial-mark: 10812086K(11901376K)] 10887844K(12514816K), 0.0001997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2015-05-26T16:23:07.321-0200: 64.425: [CMS-concurrent-mark-start]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-mark: 0.035/0.035 secs] [Times: user=0.07 sys=0.00, real=0.03 secs]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-preclean-start]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-preclean: 0.016/0.016 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-abortable-preclean-start]
2015-05-26T16:23:08.446-0200: 65.550: [CMS-concurrent-abortable-preclean: 0.167/1.074 secs] [Times: user=0.20 sys=0.00, real=1.07 secs]
2015-05-26T16:23:08.447-0200: 65.550: [GC (CMS Final Remark) [YG occupancy: 387920 K (613440 K)]65.550: [Rescan (parallel) , 0.0085125 secs]65.559: [weak refs processing, 0.0000243 secs]65.559: [class unloading, 0.0013120 secs]65.560: [scrub symbol table, 0.0008345 secs]65.561: [scrub string table, 0.0001759 secs][1 CMS-remark: 10812086K(11901376K)] 11200006K(12514816K), 0.0110730 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2015-05-26T16:23:08.458-0200: 65.561: [CMS-concurrent-sweep-start]
2015-05-26T16:23:08.485-0200: 65.588: [CMS-concurrent-sweep: 0.027/0.027 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
2015-05-26T16:23:08.485-0200: 65.589: [CMS-concurrent-reset-start]
2015-05-26T16:23:08.497-0200: 65.601: [CMS-concurrent-reset: 0.012/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

注意:年轻代Minor GC 可以与老年代并发标记清除收集器一同工作

CMS的Full GC包括7个阶段,1-5是标记阶段,会有两次stop-the-world(Initial Mark 和 Final Remark)

Phase 1: Initial Mark
初始标记:会stop-the-world,因为要标记Old Generation当中GC Roots和对年轻代对象的引用对象;GC Root直接引用的对象不多,所以很快。这是CMS期间的两大停摆事件之一。 此阶段的目标是标记Old Generation中的所有对象,这些对象要么是直接的GC ROOT,要么是从年轻代中的某些活动对象引用的。 后者很重要,因为Old Generation是单独收集的。

1:GC触发的时钟时间和相对于JVM启动的时间

2:区分GC所处阶段,CMS Initial Mark表示初始标记,标记所有Old Generation中的GC Roots

3:老年代对象数据占用的空间大小

4:老年代总的空间大小

5:堆已使用的空间大小

6:堆总的空间大小

7:初始标记的持续时间

        -user:占用CPU的时间长短

        -sys:花费在系统调用或等待系统事件的时间长短

        -real:应用挂起的时间长短

2020-12-14T09:13:05.211-0800: 1.425: [GC (CMS Initial Mark) [1 CMS-initial-mark: 4983K(118784K)] 12084K(210944K), 0.0006673 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

 
Phase 2: Concurrent Mark
并发标记:在第一阶段初始标记的基础上,从GC Roots遍历所有Old Generation的对象并且标记所有存活的对象,是并发标记,不会stop-the-world。不是所有在老年代中的存活的对象都会被标记,因为在GC在执行期间会改变引用,被改变对象引用区域叫做Card,对象被标记为dirty。由第一阶段标记过的对象出发,所有可达的对象都在本阶段标记。

2020-12-14T09:13:05.211-0800: 1.426: [CMS-concurrent-mark-start]

2020-12-14T09:13:05.216-0800: 1.430: [CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]

1:CMS-concurrent-mark:收集器执行阶段,从GC Roots遍历所有老年代对象并标记存活对象(不是所有的存活对象都会别标记,因为没有stop-the-world,应用任然可以改变对象引用)

2:并发标记占用0.35的CPU时间,0.035s的挂钟时间(从事件从开始运行到结束,时钟走过的时间,这其中包含了进程在阻塞和等待状态的时间 (挂钟时间 = 阻塞时间 + 就绪时间 +运行时间))

3:Times在并发标记中基本上无意义,因为它是从CMS-concurrent-mark-start开始,并且包含有除并发标记以外的时间消耗。

Phase 3: Concurrent Preclean

扩展阅读(https://blog.csdn.net/enemyisgodlike/article/details/106960687

不会stop-the-world,对card区域的dirty对象进行处理,并将从dirty对象可达的对象进行mark;除此之外还会做必要的对象清除和为Final Remark做准备。在本阶段,会查找前一阶段执行过程中,从新生代晋升或新分配或被更新的对象。通过并发地重新扫描这些对象,预清理阶段可以减少下一个stop-the-world 重新标记阶段的工作量。

2020-12-14T09:13:07.918-0800: 4.132: [CMS-concurrent-preclean-start]

2020-12-14T09:13:07.919-0800: 4.133: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

1:标识concurrent-preclean阶段

2:并发预清理占用0.016的CPU时间,0.016s的挂钟时间(从事件从开始运行到结束,时钟走过的时间,这其中包含了进程在阻塞和等待状态的时间 (挂钟时间 = 阻塞时间 + 就绪时间 +运行时间))

3:无意义,因为是从并发标记阶段开始计时的。

Phase 4: Concurrent Abortable Preclean.

-XX:CMSScheduleRemarkEdenSizeThreshold=2097152Bytes(2M):当Eden

不会stop-the-world,尽可能与stop-the-world的Final Remark独立开,因为这个阶段需要重复地做一些事情直到达到某个条件停止,执行时间取决于多种因素(迭代次数、做的有效工作量的数量、挂钟时间等);增加这一阶段是为了让我们可以控制这个阶段的结束时机,比如扫描多长时间(默认5秒)或者Eden区使用占比达到期望比例(默认50%)就结束本阶段。

2020-12-14T09:13:07.919-0800: 4.133: [CMS-concurrent-abortable-preclean-start]

2020-12-14T09:13:08.067-0800: 4.281: [CMS-concurrent-abortable-preclean: 0.148/0.148 secs] [Times: user=0.30 sys=0.02, real=0.15 secs]

1:区分并发标记清除垃圾收集执行阶段,这里是concurrent-abortable-preclean阶段

2:并发可中止的预清理阶段,占用0.167的CPU时间,1.074s的挂钟时间(从事件从开始运行到结束,时钟走过的时间,这其中包含了进程在阻塞和等待状态的时间 (挂钟时间 = 阻塞时间 + 就绪时间 +运行时间))

3:无意义,因为是从并发标记阶段开始计时的。

Phase 5: Final Remark

第二个会stop-the-world的阶段,最后标记在老年代所有存活的对象;为了避免多个stop-the-world的收集器连续执行,CMS尽可能在Young Generation为0k的时候执行Final Remark;

2020-12-14T09:13:05.423-0800: 1.638: [GC (CMS Final Remark) [YG occupancy: 47292 K (92160 K)]2020-12-14T09:13:05.423-0800: 1.638: [Rescan (parallel) , 0.0098615 secs]2020-12-14T09:13:05.433-0800: 1.648: [weak refs processing, 0.0000618 secs]2020-12-14T09:13:05.433-0800: 1.648: [class unloading, 0.0023545 secs]2020-12-14T09:13:05.436-0800: 1.650: [scrub symbol table, 0.0024580 secs]2020-12-14T09:13:05.438-0800: 1.653: [scrub string table, 0.0003786 secs][1 CMS-remark: 4983K(118784K)] 52275K(210944K), 0.0155274 secs] [Times: user=0.10 sys=0.00, real=0.01 secs] 

1:GC事件触发时间

2:GC事件类型:CMS Final Remark 这里表示CMS的最终标记过程,会标记在Old Generation所有存活的对象(包括在前几个标记阶段创建或修改的对象引用)

3:YG occupancy:当前年轻代对象数据占用空间大小和总的空间大小

4:Rescan(parallel):当应用挂起时,rescan并行地扫描所有的老年代中存活的对象,这里表明是并行执行并且花费了0.0085125秒

5:weak refs processing:最终标记阶段的子阶段是处理弱引用,花费的时间以及相对于JVM启动时间的时间戳

6:class unloading:卸载未使用的类,带有花费的时间和相对于JVM启动时间的时间戳

7:scrub symbol table:清理符号表以及其花费的时间

      -scrub string table:清理字符表(类级别的元数据和内部化字符串)及其花费的时间

8:CMS-remark:老年代存储对象占用空间大小和老年代当前的总共大小

9:这一GC阶段前后堆的对象数据占用空间大小以及堆的总空间大小

10:这一GC阶段的花费的时间

11: -user:cup占用时间   -sys:花费在系统调用或等待系统事件的时间

注意:到此,总共的CMS的五个标记阶段已完成

Phase 6: Concurrent Sweep

不会stop-the-world,这一阶段的主要目的是清除垃圾对象并回收垃圾对象占用的内存空间以备以后使用。

1:CMS-concurrent-sweep:清理未标记的对象并回收内存空间

2:对应的cpu占用时间和这一阶段的挂钟时间

3:无意义,记录的是从CMS标记开始至今的时间,远超过这一阶段花费的时间。

Phase 7: Concurrent Reset

并发重置,重置CMS算法内部的数据结构,为下一CMS周期做准备

2020-12-14T09:13:08.096-0800: 4.310: [CMS-concurrent-reset-start]

2020-12-14T09:13:08.096-0800: 4.310: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

1:区分CMS阶段的标志

2:这一阶段占用CPU的时间和挂钟时间

3:无意义,记录的是从CMS标记开始至今的时间,远超过这一阶段花费的时间。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值