CMS垃圾回收算法【Java】
简介
CMS垃圾回收算法是在常用的老年代垃圾回收算法之一,CMS垃圾回收算法是一种并行的垃圾回收算法,并行是指CMS在执行GC的过程中,大部分时间是和业务线程同时运行的。不同于串行化垃圾回收算法更加注重吞吐率指标,CMS垃圾回收算法主要关注的指标是暂停时间。
CMS垃圾回收算法主要分为以下流程:
- 初始标记,初始标记是从GC root开始,将所有的垃圾进行标记。
- 并发标记,GC root直接关联的对象完成标记之后,CMS垃圾回收算法就可以进行并发标记。
- 重新标记,在并发标记的过程中,业务程序还在运行,所以可能会有漏标和误标的情况发生,漏标是指应该被标记为垃圾的对象未被标记,误标是指被标记为垃圾的对象又被重新引用,重新标记主要是为了解决误标的问题。
- 并发清理的过程是和业务程序同时运行的,将系统中已经被标记为垃圾的对象进行清理,之后该空间可以释放被重新分配。
原理
初始标记
初始标记阶段是STW(Stop The World,暂停所有业务线程)的,因为初始标记阶段是将和GC root直接关联的对象进行标记,所以该阶段需要一个静态的快照,也就是需要STW。
但是因为GC Root的数据对象是一个相对固定的数量,和堆空间的大小无关,所以初始标记的时间是相对可控的。
GC Root是垃圾回收算法标记的起点,GC Root是指明确存活的不能被垃圾回收的对象,这些对象:
- 虚拟机线程栈的引用的对象,即局部变量表
- 方法区的常量以及类静态属性的变量
- JNI即本地方法栈中引用的对象。
- Java虚拟机内部的引用,入基本数据类型的Class对象,一些常驻的异常对象(比如NullPointException、OutOfMemoryError等),还有系统类加载器
- 所有被同步锁(synchronized关键字)持有的对象
- 反应Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地缓存等
在初始标记环节,CMS只标记和GC Root直接关联的对象,就是图中黄色的对象,和直接关联对象再间接关联的对象在该阶段不进行标记。
并行标记
初始标记完成之后,标记工作就可以和业务线程同时进行了。 并行标记的一般采用三色标记的方式进行标记,三色标记是指在标记过程中将对象分为白色,黑色,灰色三种。
颜色 | 定义 |
---|---|
黑色 | 对象自身扫描完成,并且完成了该对象所有引用的标记动作。 |
灰色 | 对象自身扫描完成,对象所引用的对象至少还有一个未标记完成,当标记完成之后变成黑色。
灰色是中间状态,当垃圾回收结束的时候只有白黑两种颜色。 |
白色 | 没有被垃圾回收扫描过,当扫描结束的时候白色对象可以被回收。 |
三色标记的过程如下:
- 初始阶段所有的对象都是白色。
- 初始标记完成之后将GC Roots直接关联的对象置为灰色。
- 遍历灰色对象的所有引用,当所有引用被标记完成之后,灰色对象置为黑色,该对象引用的对象置为黑色,重复该过程,直到所有的对象都变成黑色。
- 当标记过程结束,白色对象可以被回收。
三色标记算法有三类问题,多标问题,漏标问题,以及新对象的问题。
多标问题是指在垃圾回收过程中该对象是扫描到被引用的,但是过程中断开了所有的引用,此时该对象已经是事实的垃圾,但是被标记为黑色,也就是多标问题。
多标问题关联的对象一般称为浮动垃圾,其不影响垃圾回收的正确性,可以在下次垃圾回收的时候进行清理。
漏标问题是指在垃圾回收过程中,该对象被灰色对象引用,但是过程中灰色对象到该对象的引用断开,而同时又有黑色对象引用了该对象,黑色对象因为不会再进行引用对象的标记,该对象就不能被正确扫描,从而被漏标。
漏标问题一般有两种方式解决,原始快照和增量更新。
原始快照是断开灰色对象和其他对象链接的时候,会把该关联关系保存下来,扫描结束之后,使用这条关系的快照重新将该对象进行标记。
增量更新是指当黑色节点指向其他对象的时候,将这个新增的引用关系保存下来,扫描结束的时候,再重新扫描该黑色对象节点。
在垃圾回收的过程中,旧生代可能还会产生新的对象,以及会有新生代对象晋升到旧生代,CMS垃圾回收算法会在内存占用到一定阶段的时候就进行垃圾回收,所以在垃圾回收的过程中,还会预留空间给新晋对象使用,这部分区域是不参与最后的清理的。
重新标记
重新标记阶段主要是为了修正并发标记阶段的漏标问题。
CMS采用的方案是写屏障和增量更新来解决该类问题。
当黑色指向白色的引用被建立时,通过写屏障来记录引用关系,在重新标记阶段,再以引用关系里的黑色对象为根重新扫描一次即可。
为了使漏标问题不在该标记阶段再次发生,该阶段也需要STW。
并发清理
并发清理阶段将未被标记的对象占用的内存区域进行回收,但是CMS只会进行清理,不会进行内存的整理,也就是将空闲内存重新整理为大块连续的内存区域。所以CMS垃圾回收算法会产生较多的内存碎片。