标记清除法是最常用的垃圾回收方法,其整体上可以分为 “标记阶段” 和 “清除阶段” 两个阶段。
- 标记阶段常用的标记方法为三色标记法,可以参照这篇文章:自己给自己引流
- 清除阶段则根据策略不同,一般分为直接清除、复制、压缩(或整理)三种。
过程:
- 遍历标记垃圾内存
- 清除被标记的垃圾内存
优点:
- 没有额外的操作过程,速度较快
缺点:
- 会产生大量内存碎片,不利于内存复用
将内存分为From和To两部分,创建对象时,只从From部分分配内存,在垃圾回收时,将From中的存活对象复制到To的部分,清空From,并将From和To交换。
过程:
- 遍历From区域中的对象进行标记
- 将From中所有未被标记成垃圾内存的对象复制到To区域,并整理
- 清空From区域中所有内容
- From区域变成新的To区域,To区域变成新的From区域
- 不会产生内存碎片
缺点:
- 需要移动对象,当存活对象较多或者移动大对象时速度较慢
- To区域作为复制目标的备用内存始终空着,存在内存浪费
过程:
- 变量标记垃圾内存
- 清除被标记的垃圾内存
- 移动存活的对象,从内存区域的一侧依次排列
优点:
- 不会产生内存碎片
- 不需要备用内存区域,避免内存浪费
缺点:
- 依然存在因为移动对象带来的效率问题
这里依然是以JAVA环境为背景来介绍。常见的垃圾收集器及其作用代际目标如上图,对各个收集器的简单介绍可以参照这篇文章:
来自某乎的链接
CMS是针对旧生代对象(永久代可选)进行清理的GC过程,除手动调用外,主要通过周期性查询是否满足触发条件来决定是否执行。最主要的触发条件包括以下两类:
- 旧生代(或永久代)占用量达到阈值
- 初生代晋升担保失败——旧生代剩余容量不足以满足初生代对象晋升到旧生代
其核心过程如下(省略了部分):
- 初始标记(STW)
- 并发标记
- 重标记(STW)
- 并发清除
初始标记阶段
- 该阶段需要STW。
- 遍历ROOT,将所有ROOT直接可达的对象标灰添加至灰栈(To-Scanned-Set,通过Stack实现)。
- 本阶段虽然是STW,但因为只需要查找ROOT直接可达的对象,扫描对象较少,因此速度较快。
并发标记阶段
- 该阶段不需要STW,可与业务线程并发执行。
- GC线程从灰栈出发,使用三色标记法递归遍历标记所有可达对象。
- 由于当前阶段是与业务线程并发执行的,因此会存在标记过程中对象引用状态被业务线程改变造成出现错标。CMS通过增量更新的方式解决这个问题,即在黑色对象直接引用白色对象后,对白色对象设置脏卡,留待下一阶段处理。可见CMS的增量更新方法为插入写屏障机制,且为写后屏障。
- 增量更新——写后屏障——插入写屏障——强三色不变原则
- 本阶段扫描对象巨大,时间最久,但由于是并发执行,因此并不阻断程序。
重标记阶段
- 该阶段需要STW。
- 以脏卡对象、栈上对象、初生代对象为出发点,重新进行递归扫描 扫描过程会跳过并发标记阶段已经处理过的对象
- 本阶段主要目的是为了处理并发标记阶段中被业务线程改变的引用,由于本阶段扫描过程中业务线程被停止运行,因此不会产生新的错标问题。
- 本阶段扫描数量较多,用时较长,仅次于并发标记阶段,且由于是STW的,对程序阻断明显,也是GC过程中卡顿的最主要来源。
- 为降低该阶段处理对象的数量,实际会在并发标记极端结束和重标记阶段开始之前插入两个预处理阶段。
并发清除阶段
- 该阶段不需要STW,可与业务线程并发执行。
- 此时标记阶段已经完全结束,GC线程清除此前被标记的垃圾内存,同时业务线程可并发执行。
- 由于业务线程与清理线程在同时工作,为避免出现程序错误,CMS的清理采用的是基础的标记清理,不需要移动对象,但会产生内存碎片。
- 此阶段由于要清理大量垃圾内存,用时也较长,但因为与业务线程并发执行,因此也不会阻断程序。
上文提到,CMS是清理旧生代的Old GC,且会产生内存碎片。对于产生的内存碎片,CMS本身并不做处理,而是在JVM进行Full GC的时候通过标记压缩的方式进行处理。
CMS提供了两个参数可用于配置对内存碎片的清理频率:
-XX:+UseCMSCompactAtFullCollection Full GC时会进行内存压缩整理
-XX:CMSFullGCsBeforeCompaction 每执行指定次数的普通Full GC后,执行一次进行内存压缩整理的Full GC
最后再贴一段
以上就是本篇文章【3. CMS(Concurrent Mark Sweep)】的全部内容了,欢迎阅览 ! 文章地址:http://w.yusign.com/quote/7170.html 行业 资讯 企业新闻 行情 企业黄页 同类资讯 网站地图 返回首页 述古往 http://w.yusign.com/mobile/ , 查看更多CMS收集器对CPU资源非常敏感。其实,面向并发设计的程序都对CPU资源比较敏感。
在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。
CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个(譬如2个)时,CMS对用户程序的影响就可能变得很大,如果本来CPU负载就比较大,还分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,其实也让人无法接受。
为了应付这种情况,虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器变种,所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思想一样,就是在并发标记、清理的时候让GC线程、用户线程交替运行,尽量减少GC线程的独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得少一些,也就是速度下降没有那么明显。
实践证明,增量式的CMS收集器效果很一般,在目前版本中,i-CMS已经被声明为"deprecated",即不再提倡用户使用。
引用自《深入理解java虚拟机》 周志明著