CMS 收集器
老年代垃圾收集器,使用标记-清除算法,并发收集。
CMS
(Concurrent Mark Sweep
)收集器是一种以获取最短回收停顿时间为目标的收集器。
疑问,图里为啥可以并发的阶段也要标出Safepoint
因为线程是在
Safepoint
停下来的,又是在Safepoint
启动了。
收集过程
- 初始标记(
CMS initial mark
)stw
- 并发标记(
CMS concurrent mark
) - 重新标记(
CMS remark
)stw
- 并发清除(
CMS concurrent sweep
)
整个过程中耗时最长的是并发标记和并发清除过程。但这两个过程用户线程都不需要停顿。
CMS
的缺点
CMS
是一款优秀的收集器,优点就是并发收集、低停顿,被称为“并发低停顿收集器”。
CMS
是HotSpot
虚拟机追求低停顿的第一次成功尝试。但也有明显缺点。
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
时都进行碎片整理)