目录
一.执行步骤
1.初始标记(stw):只标记从gc root直接引用的对象。
2.并发标记:从初始标记的对象出发,继续标记。如果标记期间有发生晋升、老年代直接分配、引用关系变更等,会记录dirty card。
3.并发预清理:标记在上一阶段新生代新分配的对象到老年代的引用;遍历dirty card,根据dirty card的当前引用关系,重新标记,目的是减轻最终标记stw时间。
4.可中断的并发预清理:不断重复预清理的过程,直到时间到达CMSMaxAbortablePrecleanTime(默认5s)设置的阀值,或是迎来一次young gc。
5.最终标记(stw):遍历gc root ,重新标记(此处我的理解是遇到之前标记过的就停止);遍历dirty card,重新标记。
6.并发清理:清理上一阶段未被标记的对象。
7.并发重置:清理本次cms gc的上下文信息,为下一次gc做准备。
二.优点和缺点
优点:并发收集,降低stw时间。
缺点:1.并发时占用cpu 。
2.无法处理浮动垃圾(已被标记对象转换成的垃圾)。
3.cms基于标记清除算法,会产生空间碎片。
三.关键点和重要参数
1.在最终标记时会遍历young区所有对象,如果此时young区对象较多,会耗时较长,有两种解决办法。
第一个,是等待在可中断的并发预清理阶段执行一次young gc,CMSScheduleRemarkEdenSizeThreshold(默认2M)、CMSScheduleRemarkEdenPenetration(默认50%),CMSMaxAbortablePrecleanTime(默认5s),CMSMaxAbortablePrecleanLoops(默认0),通过这几个参数来控制该阶段是否执行与执行的计划(不过我自己做了实验CMSScheduleRemarkEdenPenetration这个参数并没有起到应有的效果,此处有些困惑)。
第二个,可以利用cms提供的-XX:+CMSScavengeBeforeRemark参数来控制在最终标记前执行一次young gc(并不会绝对执行,jvm会做一定判断)。
2.产生较多的空间碎片
空间碎片过多可能引发promotion failed (空间担保失败、晋升失败)和 concurrent mode failure 异常,此时cms gc会降级成full gc,这里的full gc是使用标记清除算法。可通过-XX:UseCMSCompactAtFullCollection,-XX:CMSFullGCBeforeCompaction 来设置是否开启full gc的整理阶段以及几次full gc后进行一次整理(默认开启,每次都进行整理)。
还可以通过减小cms gc阀值和增大老年代空间来降低碎片产生的影响。
3.触发cms gc的时机
通过-XX:+UseCMSInitiatingOccupancyOnly 和 -XX:CMSInitiatingOccupancyFraction 来控制,如果不设置这两个参数,jvm会自行判断cms gc何时执行,这种情况下出现问题不好排查。