深入理解JVM之CMS垃圾回收器

概述

CMS(concurrent mark sweep)并发标记清理收集器以获取最短回收停顿时间为目标的收集器,特别适用于互联网网站或者B/S系统的服务端上,这类重视服务响应速度的,给用户良好的体验的情况。使用多线程并发地标记和清理,基于标记-清理(mark-sweep)的算法进行垃圾回收。现代JVM中,主要有3中垃圾收集算法。如下:

  1. 标记-清理(mark-sweep),清理的垃圾的时候,回收标记阶段标记的所有对象。
  2. 复制(copying),将内存区域按比例划分为1个Eden区和2个Survivor区(即from区和to区)。执行复制算法时,把存活的对象复制到1个Survivor区,然后,清理剩余的内存。
  3. 标记-整理(mark-compact),由于标记-清理在回收后会产生内存碎片,衍生出的标记-整理算法。在垃圾回收时,将标记的需要回收的对象移动到空间的一段,然后释放剩余的空间。

在垃圾回收时,JVM根据不同类型的对象,采用不同的收集策略。采用分代收集,将不同类型的对象分配到不同的区域,主要的分代为新生代(Young Generation),老年代(Old Generation),永久代(Permanent Generation)。垃圾收集作用在不同的分代,垃圾收集类型分为两种,如下:

  1. Minor Collection,对新生代进行收集。
  2. Major Collection,对老年代或永久代进行收集。
  3. Full collection,对所有的分代都进行收集。首先,按照新生代配置的收集算法对新生代进行收集;接着,使用老年代收集算法对老年代或永久代进行收集。Full Collection比较耗时。

运行流程

CMS收集器,其运行地步骤如下:

  1. 初始标记(CMS initial mark),停止所有用户线程(stop the world),标记GC roots能够关联地对象。
  2. 并发标记(CMS concurrent mark),与用户线程同时运行,并发地标记GC roots能够关联地对象。
  3. 重新标记(CMS remark),停止所有用户线程(stop the world),修正并发标记期间,用户线程修改地GC roots能够关联地对象。
  4. 并发清楚(CMS concurrent sweep),与用户线程同时运行,并发地GC。

流程图如下:

CMS特性 

  • CMS收集器对CPU资源非常敏感

在并发阶段, 它虽然不会导致用户线程停顿, 但是会因为占用了一部分线程( 或者说CPU资源) 而导致应用程序变慢, 总吞吐量会降低。 CMS默认启动的回收线程数是( CPU数量+3) /4, 也就是当CPU在4个以上时, 并发回收时垃圾收集线程不少于25%的CPU资源, 并且随着CPU数量的增加而下降。 但是当CPU不足4个( 譬如2个) 时, CMS对用户程序的影响就可能变得很大, 如果本来CPU负载就比较大, 还分出一半的运算能力去执行收集器线程, 就可能导致用户程序的执行速度忽然降低了50%, 其实也让人无法接受。

  • 无法容纳浮动垃圾( Floating Garbage)而产生一次Full GC

由于CMS并发清理阶段,其他的用户线程还在并发运行着,用户线程会不断产生新的垃圾,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉,这一部分垃圾就称为浮动垃圾( Floating Garbage)。 JVM中参数-XX:CMSInitiatingOccupancyFraction的值用来设置CMS收集器的启动阈值, 在JDK 1.6中, CMS收集器的启动阈值为92%。如果老年代的内存容量达到了该设置的阈值,就会触发CMS收集器进行垃圾回收操作。此时,如果老年代剩余的8%的内存容量无法容纳用户线程产生的浮动垃圾,就会出现“Concurrent Mode Failure”失败。这时JVM虚拟机启用Serial Old收集器(使用单线程,基于标记-整理算法回收老年代,并且会停止所有用户线程)来重新进行老年代的垃圾收集, 产生一次Full GC。

  • 无法容纳大对象,而产生一次Full GC

CMS是一款基于标记-清除算法实现的收集器,GC操作结束后会有大量的空间碎片产生。 空间碎片过多时, 在老年代创建大对象时,如果没有连续空间来分配当前对象。此时,会产生一次Full GC。JVM参数-XX:+UseCMSCompactAtFullCollection 默认就是开启的,用于在CMS收集器无法创建大对象要进行FullGC时,开启内存碎片的合并整理过程。JVM参数-XX: CMSFullGCsBeforeCompaction用于设置执行多少次不压缩的Full GC后, 跟着来一次带压缩的。(默认值为0, 表示每次进入Full GC时都进行碎片整理。

  • 担保失败,无法接受年轻代存活的对象,而产生一次Full GC

在年轻代进行Minor GC时,如果Survivor无法容纳的存活对象,由于老年代的担保机制,这些Survivor无法容纳的存活对象会直接进入老年代。如果老年代内存不足,导致担保失败( Handle Promotion Failure),这时,CMS会重新发起一次Full GC。

CMS相关命令

-XX:UseConcMarkSweepGC,对老年代使用CMS进行回收。

-XX:ParallelGCThreads,并行执行地GC线程数量。

-XX:UseParNewGC,对新生代使用并行线程收集,配合CMS老年代收集。即使不是开启该选项,当选择CMS后,新生代默认也会使用ParNew收集器进行回收。

-XX:CMSInitiatingOccupancyFraction,CMS收集器的启动阈值。

-XX:+UseCMSCompactAtFullCollection,默认就是开启的,用于在CMS收集器无法创建大对象要进行FullGC时,开启内存碎片的合并整理过程。

-XX:CMSFullGCsBeforeCompaction,设置执行多少次不压缩的Full GC后, 跟着来一次带压缩的。(默认值为0, 表示每次进入Full GC时都进行碎片整理。

发布了37 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/new_com/article/details/105334957
今日推荐