CMS 垃圾收集器
这两天在网上看了不少关于JVM 垃圾回收的相关知识,大家总结的都很好,我也从中学到了很多,现在对其中的一些内容进行一下总结。
这篇文章先总结一下CMS垃圾回收器,把自己所理解的写出来,加深认识。
首先CMS 指的是 Concurrent Mark Sweep,并发标记清除。
这里虽然说得是并发,但是并不是整个过程都是并发的,在cms中仍然后两个阶段是需要stw的,只不过它把耗时的操作放到了并发的阶段中,使stw的时间很大程度的缩短了。
CMS的阶段有
1. 预标记阶段 (initial mark) stw
2. 并发标记
3. 并发预清理 (需要通过参数-XX:+CMSParallelRemarkEnabled来开启)
4. 重标记阶段 stw
5. 并发清理阶段
6. 重置阶段
预标记阶段
进行可达性分析,标记出GC Root直接饮用的对象
并发标记
进行GC Root tracing,和用户线程并发执行,由前一阶段标记处的对象出发,找出所有的可达对象
并发预处理
这个阶段可以通过配置来进行控制,这个阶段的作用和重标记类似,是为了把重标记的部分工作放到这里,减少重标记阶段的停顿时间。
PS
在进行可达性分析的过程中,可能会遇到两种特殊的情况 1.年轻带中的对象引用了老年代中的对象(比较常见)2.老年代中的对象引用了年轻代中的对象。
所以在对老年代进行回收的时候,也要对年轻代进行扫描。所以在这个操作中为了减少扫描年轻代的时间,会先对年轻代进行一次 minor gc(有参数控制XX:+CMSScavengeBeforeRemark)
在对年轻代进行gc的时候,如果也要对老年代进行扫描的话,那停顿的时间就会非常长了。在这里jvm有一个CardTable的数据结构可以高效的实现这种操作。
为了支持高频率的新生代的回收,虚拟机使用一种叫做卡表(Card Table)的数据结构,卡表作为一个比特位的集合,每一个比特位可以用来表示年老代的某一区域中的所有对象是否持有新生代对象的引用。
这样新生代在GC时,可以不用花大量的时间扫描所有年老代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有卡表的标记位为1时,才需要扫描给定区域的年老代对象。而卡表位为0的所在区域的年老代对象,一定不包含有对新生代的引用。
所以在进行老年代gc的时候通常也会进行minor gc