CMS 收集器

老年代垃圾收集器,使用标记-清除算法,并发收集。

CMSConcurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。

疑问,图里为啥可以并发的阶段也要标出Safepoint

因为线程是在Safepoint停下来的,又是在Safepoint启动了。

收集过程

  1. 初始标记(CMS initial markstw
  2. 并发标记(CMS concurrent mark
  3. 重新标记(CMS remarkstw
  4. 并发清除(CMS concurrent sweep

整个过程中耗时最长的是并发标记和并发清除过程。但这两个过程用户线程都不需要停顿。

CMS的缺点

CMS是一款优秀的收集器,优点就是并发收集、低停顿,被称为“并发低停顿收集器”。

CMSHotSpot虚拟机追求低停顿的第一次成功尝试。但也有明显缺点。

CMS收集器对处理器资源非常敏感

事实上,面向并发设计的程序都对处理器资源比较敏感。

在并发阶段虽然用户线程不会停顿,但会因为占用部分线程而导致应用程序变慢,降低吞吐量。

CMS默认启动的回收线程树是(处理器核心+3)/ 4。处理器核心在四个或以上,垃圾收集线程只占用不超过25%的处理器运算资源。

但当处理器核心数不足4个时,CMS对用户程序的影响就可能变得很大。如果应用本身的处理器负载就很高,还要分出一般运算能力

去执行垃圾收集,会导致用户程序执行速度忽然大幅降低。

为了缓解这种情况,虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器变种,

在并发标记、清理的时候让收集器线程和用户线程交替运行,尽量减少垃圾收集线程的独占资源的时间,这样整个垃圾收集过程会变长,

但对用户程序的影响会显得较少一些。实践证明,效果一般,JDK7开始,i-CMS就被声明为deprecated,在JDK9被完全废弃。

无法处理浮动垃圾(Floating Garbage)

浮动垃圾:CMS并发标记和并发清理阶段,用户线程还在运行,运行就可能有新的垃圾对象产生,这部分对象是在标记过程结束后的,

CMS无法在当次收集中处理它们,只能留到下一次垃圾收集时再清理掉。这部分垃圾就是浮动垃圾。

Concurrent Mode Failure:同样由于垃圾收集阶段用户线程还在执行,所以需要预留足够内存空间提供给用户线程,

如果预留的空间无法满足程序分配新对象的需求,就会出现一次“并发失败”(Concurrent Mode Failure),这时虚拟机不得不启动预备方案,

冻结用户线程的执行,临时启动Serial Old收集器重新进行老年代的垃圾收集。提前触发一次Full GC

预留的空间:在JDK5默认配置下,CMS收集器当老年代使用了68%的空间后就会被激活,可以通过-XX:CMSInitiatingOccupancyFraction值来提高

CMS的触发百分比,降低回收频率。到JDK6默认值提升至92%。

CMS无法像其他垃圾收集器一样等待整个老年代几乎被填满再进行收集。

大对象分配问题

CMS是一款基于“标记-清除”算法实现的收集器,意味着进行垃圾收集后是会产生空间碎片的。

空间碎片过多时,往往无法找到足够的连续空间来分配大对象,进而不得不触发一次Full GC

为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关的参数

(默认开启,JDK9开始废弃),用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程,

由于移动对象CMS收集器无法并发,因此这样会导致停顿时间变长,因此还有一个-XX:CMSFullGCBeforeCompaction

(JDK9开始废弃),这个参数要求CMS收集器在执行过若干次不整理空间的Full GC之后,下一次Full GC前会先

进行碎片整理(默认值是0,表示每次进入Full GC时都进行碎片整理)