CMS为什么采用标记-清除算法

年老代并发收集器-CMS

-XX:+UseConcMarkSweepGC
新生代:复制算法,默认搭配ParNewGC,并行
年老代:标记-清除,并发(如果发生Concurrent Mode Fail,则使用SerialOld做后备收集器)

并行与并发的区别

CMS的执行周期

  • 初始标记:STW,单线程,由于是从GCRoot寻找直达的对象,速度快

  • 并发标记:与应用线程一起运行,是CMS最主要的工作阶段,通过直达对象,扫描全部的对象,进行标记

  • 重新标记:STW,修正并发标记时由于应用程序还在并发运行产生的对象的修改,多线程,速度快,需要全局停顿

  • 并发清除:与应用程序一起运行,为何采用清除算法?CMS主要关注低延迟,因而采用并发方式,清理垃圾时,应用程序还在运行,如何采用压缩算法,则涉及到要移动应用程序的存活对象,此时不停顿,是很难处理的,一般需要停顿下,移动存活对象,再让应用程序继续运行,但这样停顿时间变长,延迟变大,所以CMS采用清除算法。

后备收集器:SerialOld

并发GC,吞吐量下降,采用标记清除,碎片多,占用额外内存,不能在堆空间满时清理,触发GC:
清理时,应用程序还在运行此时如果预留的空间不够应用程序申请的空间的话,则会触发Concurrent Mode Fail,此时便会启用后备收集器:SerialOld进行GC,产生全局停顿

并发模式失败(Concurrent Mode Failure)

CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。

(1)如果对象提升到年老代的速度太快,而CMS收集器不能保持足够多的可用空间时,就会导致年老代的运行空间不足;
(2)当年老代的碎片化达到某种程度,使得没有足够空间容纳从新生代提升上来的对象时,也会发生并发模式失败。

当发生并发模式失败时,年老代将进行垃圾收集以释放可用空间,同时也会整理压缩以消除碎片,这个操作需要停止所有的java应用线程,并且需要执行相当长时间。

浮动垃圾

由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。

CMSInitiatingOccupancyFraction

也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用

在JDK 1.5的默认设置下,CMS收集器当老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数从而获取更好的性能,

在JDK 1.6中,CMS收集器的启动阈值已经提升至92%。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。

所以说参数-XX:CMSInitiatingOccupancyFraction设置得太高很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。

CMS内存整理

CMS是一款基于“标记—清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。

空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数(默认就是开启的),用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长。

虚拟机设计者还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)。

Promotion Failure

过早提升(Premature Promotion),MinorGC过程中,Survivor可能不足以容纳Eden和另外一个Survivor中存活的对象,如果Survivor中的存活对象溢出,多余的对象将被移到年老代。

在MinorGC过程中,如果年老代满了无法容纳更多的对象,则MinorGC之后,通常会进行FullGC,这将导致遍历整个java堆,这称为提升失败(Promotion Failure)

你可能感兴趣的:(jvm)