原文出处:Concurrent Mark and Sweephtml
这种垃圾收集器的官方名称是“Mostly Concurrent Mark and Sweep Garbage Collector”。它在Yong代使用
并行,“stop-the-world”、标记复制算法,在Old代使用并发标记清除算法。java
设计这种算法目的是,在回收Old代的时候去避免长时间停顿。他的实现分为两方面。第一,它不清楚Old代,而是使用空闲列表管理回收空间。第二,它在标记清除期间的大部分工做和应用程序并发执行。这意味着执行这些阶段,收集器不会刻意停顿应用线程。应当注意的是,它依然和应用程序抢占CPU时间片。默认的,这种算法的使用的线程数等于你的机器物理核心数的¼。算法
你在命令行经过以下图所示的选项显式指定这个收集器:数据结构
java -XX:+UseConcMarkSweepGC
若是你有意向的话,在多核机器上使用这样的组合是个不错的选择。应用用户能够明显感知个别GC阶段停顿时长减小带来的变化,快速响应给他们带来更好用户体验。大多时候,GC消耗至少几个CPU资源,而不是执行你的应用代码。在单核应用中CMS性能一般低于Parallel。并发
就拿上面的GC算法来讲,让咱们看一下这种算法在实践应用当中Minor GC和Major GC阶段如何工做:oracle
2015-05-26T16:23:07.219-0200: 64.322: [GC (Allocation Failure) 64.322: [ParNew: 613404K->68068K(613440K), 0.1020465 secs] 10885349K->10880154K(12514816K), 0.1021309 secs] [Times: user=0.78 sys=0.01, real=0.11 secs] 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 GCjvm
从日志看出,GC事件第一步操做是Minor GC清理Young代。咱们分析一下面示意图中收集器是如何工做的:性能
经过上面咱们能够看出,回收前heap区已使用空间10,885,349K,Young代已使用空间613,404K。这意味着Old代可用空间是10,271,945K。回收后,Young代减小545,336K可是heap区仅仅减小5,195K。这意味着540,141K是Yong代升迁到Old代的对象的大小。spa
Full GC操作系统
如今,你已经习惯阅读GC日志,这章介绍日志中另一种形式的垃圾收集器。下图中冗长的输出囊括了Old代主要垃圾收集器不一样阶段。咱们不一次性地简单归纳日志事件,相反,咱们一条一条分析日志的不一样阶段。回顾一下,CMS收集器相似于下图:
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]
但要记住,实际上Old代并发收集期间的任什么时候候,Young代都会发生Minor GC。在这种状况下,能够看到上面的Major GC和Minor GC 事件交替执行。
阶段1:初始化标记。CMS期间两次“”stop-the-world“”中的一次在这个阶段发生。这个阶段的目标是标记Old代全部的GC root可达对象,Young代中被引用的对象。Old代独立回收这点很重要。
阶段2: 并发标记。这个阶段期间,垃圾回收器扫描整个Old代,标记全部存活对象,从上一个“”Initial Mark“”阶段发现的全部Root对象。“”Concurrent Mark“”阶段,正如名称所示,和应用线程并发运行,不会停顿应用线程。因为标记期间应用改变对象引用,Old代不是全部存活对象也许被标记。
上面的示意图说明,标记线程并发地移除没有被“”Current obj“”指针引用的对象。
阶段3:并发清除 。这是一个并发阶段,和应用线程并行执行,不会引发线程停顿。虽然上一个阶段和应用线程并发执行,可是一些引用被改变。不管什么时候发生,JVM标记heap区(“”Card“”)包含突变成“”dirty“”的对象(了解 Card Marking)。
在预清理阶段,这些“”脏“”对象被占用,被他们引用的对象依然被标记,当它释放后,Card被清除。
补充一点,一些必要的最后标记的准备工做被执行。
阶段4:并发预处理。又一个并发的,不须要“”stop-the-world“”的阶段尝试去尽量多消除最终标记因为停顿的影响。因为他作循环作着一样的事情直到遇到一个中断式条件(例如迭代次数,有用工做完成的数量,钟墙时间消耗,等等),所以这个阶段的准确好事取决于这些因素。
这个阶段会明显影响到即将带来的“”stop-the-world“”阶段,有不少的配置选项和失败模式。
阶段5:最终标记。这是第二个也是最后一次事件发生期间“”stop-the-world“”的阶段。“”stop-the-world“”的目的是最后标记Old代中全部的存活对象。因为预处理阶段是并发的,它们也许赶不上应用线程改变的速度。“”stop-the-world“”须要完成这些考验。
一般当年轻代为了尽量多清除几回紧接着发生的几回“”stop-the-world“”而CM去执行最终标记。
此次事件看起来比以前的阶段复杂得多:
最后标记阶段事后,Old代全部存活对象被标记,垃圾收集器去准备去回收Old代中的无用对象。
阶段6:并发清除。和应用线程并发执行,不须要“”stop-the-world“”。这个阶段的目的清除无用对象,以备之后使用。
阶段7: 并发重置。并发执行阶段,重置CMS算法内部的数据结构,为下一个周期作准备。
总而言之,CMS收集器经过并发线程卸载大量工做而不用去停顿应用线程在减小阶段运行时间上作了伟大的工做。然而,他也存在一些缺点,最值得的注意的是形成Old代碎片化,阶段持续时间在某些状况下缺少可预见性,尤为在大heap区。