CMS GC的官方名称为“Mostly Concurrenct Mark and Sweep Garbage Collector”(最大-并发-标记-清除-垃圾收集器)。
作用范围: 老年代
算法: 并发标记清除算法。
启用参数:-XX:+UseConMarkSweepGC
默认回收线程数:(处理器核心数量 + 3)/4
Java9之后使用CMS垃圾收集器后,默认年轻代就为ParNew收集器,并且不可更改,同时JDK9之后被标记为不推荐使用,JDK14就被删除了。
并发与并行有什么区别?
并行(Parallel):并行描述的多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工作,通常默认此时用户线程为等待状态。
并发(Concurrent):并发描述的垃圾收集线程与用户线程之间的关系,说明同一时间垃圾收集线程与用户线程都在工作,由于用户线程并未冻结,因此还能继续相应服务请求,但由于垃圾收集器线程占用了一定的系统资源,此时应用程序处理的吞吐量将受到一定影响。
设计目标/优点:避免在老年代垃圾收集时出现长时间的卡顿,主要通过两种手段来达成此目标:
第一,不对老年代进行整理,而是使用空闲列表(free-list)来管理内存空间的回收
第二,在mark-and-sweep(标记-清除)阶段的大部分工作和应用线程一起并发执行。
适用场景:
GC过程短暂,低延迟,适合对延迟要求较高的系统
如果服务器是多核CPU,并且主要调优目标是降低GC停顿导致的系统延迟,那么使用CMS是个很明智的选择。通过减少每一次GC停顿的时间,很多时候会直接改善用户体验。因为多数情况下有部分CPU资源被垃圾回收器线程消耗,所以在CPU资源受限的情况下,CMS GC会比并行GC的吞吐量差一些(对于绝大部分系统,这个吞吐和延迟的差别应该都不明显)
在实际情况中,进行老年代的并发回收时,可能会伴随多次年轻代的minor GC。在这种情况下full GC的日志中就会掺杂着多次minor GC事件
1、初始标记(CMS initial mark)
2、并发标记(CMS concurrent mark)
3、重新标记(CMS remark)
4、并发清除(CMS concurrent sweep)
这个阶段会STW。
工作模式: JDK7之前单线程,JDK8之后多线程
目标: 标记所有的根对象,包括根对象直接引用的对象,以及被年轻代中所有存活的对象所引用的老年代对象(只是标记一下GC Roots能直接关联到的对象,速度很快)
此阶段,CMS GC遍历所有的对象,标记存活的对象,从前一阶段“初始标记”找到的根元素开始算起。“并发标记”阶段就是与应用程序同时运行,不用暂停的阶段。此阶段由于与用户线程并发执行,对象的状态可能会发生变化,如下:
年轻代的对象从年轻代晋升到老年代
有些对象被直接分配到老年代
老年代和年轻代的对象引用关系变化
JVM会通过Card(卡片)的方式将发生改变的老年代区域标记为“脏”区,这就是所谓的卡片标记(Card Marking)
在上边的图中,“当前处理的对象”的一个引用就被应用线程给断开了,即这个部分的对象关系发生了变化
阶段3:并发预清理
也是用于标记老年代存活的对象,此阶段仍然是与应用线程并发执行的,不需要停止应用线程。
目的: 让最终/重新标记的STW时间尽可能短
标记目标:
老年代中在并发标记中被标记为“dirty”的card
幸存区(from和to)中引用的老年代对象
关闭参数:-XX:-CMSPrecleaningEnabled,默认开启
阶段4:可取消的并发预清理