CMS全名叫做Concurrent Mark Sweep,是一种以获取最短回收停顿时间为目标的收集器。在G1之前,大部分互联网网站或者基于浏览器的B/S系统的java服务器会采用CMS作为老年代垃圾收集器。
CMS垃圾收集基于标记-清除算法实现,整个过程分为四个步骤,包括:
1)初始标记
- 标记GC Roots能直接关联到的对象
- 需要STOP THE WORLD
2)并发标记
- 从GC ROOTS的直接关联对象开始遍历整个对象图
3)重新标记
- 修正并发标记期间因用户程序继续运作而导致产生变动的那部分对象的标记记录(采用增量更新方式解决三色标记法可能产生的错标问题)
- 需要STOP THE WORLD
4)并发清除
- 清理删除标记阶段判断已经死亡的对象,由于不需要移动存活对象,可以和用户线程同时发起

CMS的几个明显的缺点:
1、触发”并发失败”(Concurrent Mode Failure)的风险
基于两个原因,CMS在运行时需要预留一部分的空间
- CMS的并发标记和并发清理阶段,用户线程还是运行的,就会有新的垃圾产生(浮动垃圾)
- 垃圾收集阶段用户线程还要持续运行,就需要预留足够空间提供给用户线程使用
在JDK5中的默认设置下,CMS收集器当老年代使用了68%的空间后就会被激活,但这个配置偏保守,如果在实际应用中老年代增长并不是很快,可适当调高相应参数。在JDK6时,这个阈值默认提升至92%。但是这会带来一个风险:
如果CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败”,这时候虚拟机会冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集,但是这样停顿时间就变得更长。
所以参数-XX:CMSInitiatingOccupancyFraction在生产环境中要根据实际应用情况来权衡设值
2、内存碎片
CMS是一款基于”标记清除”算法实现的收集器,这意味着收集结束时会有大量的空间碎片。空间碎片过多时,会给大对象的分配带来麻烦。会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不触发Full GC。
为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开发参数(默认开启,JDK9开始废弃),用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程。这样空间碎片问题是解决了,但是停顿时间又会变长,因此CMS还提供了一个参数-XX:CMSFullGcsBeforeCompaction(JDK9开始废弃),作用是CMS在执行过若干次(数量由参数值确定)不整理空间的Full GC之后,下一次进入Full GC会先进行碎片整理(默认值为0,表示每次进入Full GC时都进行碎片整理);
3、对处理器资源的消耗(CPU资源比较少时需要关注)
面向并发设计的收集器对处理器的占用都比较大,特别是当处理器资源比较少时(不足四个)。因为在并发阶段,虽然它不会导致用户线程停顿,但却因为占用了一部分处理器的计算能力而导致应用程序变慢,降低总吞吐量。
参考
1、《深入理解JAVA虚拟机》
-------------本文结束,感谢您的阅读-------------