暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JVM之垃圾收集器(CMS收集器)

林若心夕 2020-01-17
260

CMS收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来更好的体验。CMS服务器就非常符合这类应用的需求。
CMS服务器是基于标记-清除算法实现的,它的运作过程分为4个步骤:初始标记,并发标记,重新标记,并发清除。
其中初始标记和重新标记还是需要"Stop The World"。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间会比初始标记时间长一点,但远比并发标记的时间短。
由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。CMS运作流程如下图所示:

图1 CMS收集器运作流程

CMS收集器的主要优点是:并发收集,低停顿。但是它也有比较明显的缺点:
在并发阶段,CMS收集器虽然与用户线程并行,不会导致停顿,但是会因为占用了部分内存而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4,回收内存占比为1/4+3/(4*CPU数量),也就是说回收内存占比随着CPU数量的增加而下降,但是当CPU不足4个时,CMS对用户程序的影响就会变得很大。
CMS收集器无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致另一次Full GC。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行还有新的垃圾产生,这部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好等待下一次GC,这部分垃圾就称为浮动垃圾。也是由于在垃圾收集阶段用户线程还在运行,所以需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等老年代几乎填满了再进行收集,需要预留一部分空间给用户线程使用。虚拟机有参数-XX:CMSInitiatingOccupancyFraction,用于设置触发CMS收集器启动的老年代使用空间的百分比,如果应用中老年代增长的不是太快,可以适当调高这个参数的值来提高触发百分比,以便降低内存回收次数而获取更好的性能。如果这个值过大,CMS运行期间预留的内存无法满足程序需要,就会出现"Concurrent Mode Failure",这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新收集老年代垃圾,这样停顿的时间就更长了。
另外,CMS基于标记-清除算法实现,意味着会有大量内存碎片产生,不利于给大对象分配内存,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了-XX:+UseCMSCompactAtFullCollection开关参数(默认开启),用于在CMS收集器顶不住要进行FullGC时开启内存碎片合并整理过程,内存整理过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长。虚拟机还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数用于设置执行多少次不压缩的FullGC后,跟着来一次带压缩的(默认值为0,表示每次进入FullGC时都进行碎片整理)。


文章转载自林若心夕,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论