- 基本说明:
- 目标:获取最短回收停顿时间
- 算法:标记-清除算法
- 线程:并发
- 步骤:
- 初始标记:(会STP)
- 标记一下 GC Roots 能直接关联到的对象,速度很快
- 并发标记:(耗时最长,且可与用户线程一起工作)
- 重新标记:(会STP)
- 修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。
- 时间:比初始标记稍长,远比并发标记时间短。
- 并发清除:(耗时最长,且可与用户线程一起工作)
- 总体来说:可以看作,CMS的内存回收过程是与用户线程一起并发执行的。
- 缺点:
- 1、对CPU资源敏感
- 问题:并发阶段虽然用户线程不停顿,但会占用CPU资源导致用户线程变慢,吞吐量降低。
- 默认回收线程数:(CPU数量+3)/4。
- 当CPU>4时,并发线程>25%的CPU资源。且随CPU数量增加而下降。
- 当CPU<4时(假设为2),并发线程>50%的CPU资源,很影响用户体验。
- 解决:
- (不提倡使用)提供“增量式并发收集器”:并发标记和并发清除阶段让GC线程和用户线程交替运行,减少GC线程的独占资源时间。会增长GC时间,但降低用户影响。
- 2、无法处理浮动垃圾:
- 1、浮动垃圾:进行并发清除时用户线程运行产生的垃圾。只能在下一次GC时再清理。
- 2、并发清理阶段用户线程运行需要预留空间,老年代没有填充满就会进行GC。
- JDK1.5:该GC启动百分比阈值为68%
- JDK1.6:该GC启动百分比阈值为92%
- 可通过:-XX:CMSInitiatingOccupancyFraction配置(太高会引发大量问题3)。
- 3、老年代GC如果预留空间不足,会出现“Concurrent Mode Failure”,此时虚拟机会启动后被预案,临时启用 Serial Old 收集器,会导致停顿时间变长。
- 3、基于标记-清除算法:
- 问题:会产生空间碎片。大对象分配会因无法找到连续内存空间而触发FGC
- 解决:
- +UseCMSCompactAtFullCollection参数:在CMS要进行FGC时开启内存碎片的合并整理过程。默认开启。
- -XX:CMSFullGCsBeforeCompaction参数:设置N次不压缩的FGC后跟着来一次带压缩的FGC。默认为0,即每次FGC都进行碎片整理。