前言
本篇主要介绍JVM内存回收算法,理论内容都节选自《深入理解java虚拟机》这本书,它是下一篇JVM垃圾回收器的基石。
一、分代收集
1.分代收集理论
分代收集理论分别是:
- 弱分代假说,绝大多数对象都是朝生夕死;
- 强分代假说,熬过越多次垃圾收集过程的对象就越难以消亡;
- 跨代引用说,跨代引用相对于同代引用来说仅占极少数。
其中上述前2个假说共同奠定了常用垃圾收集器的一致设计原则:收集器应该将java堆划分不同区域,按照对象的年龄分配到不同区域存储,针对不同区域使用不同回收算法。
而跨代引用说解决了存活算法效率问题,即 可达性分析算法对于少量跨代引用不去跨代扫描整个区域。而是使用全局数据结构remember set来管理这些少量跨代引用的对象。
2.垃圾收集
由于分代收集理论常用的垃圾回收器都会将java堆分为多个区域(新时代和老年代),则针对不同区域会有不同的垃圾收集,常见如下:
- Minor GC 新生代收集,目标只是新生代的垃圾收集;
- Major GC 老年代收集,目标是老年代的垃圾收集;
- Full GC 收集整个java堆和方法区的垃圾收集;
- Mixed GC 收集整个新生代以及部分老年代的垃圾收集,仅G1支持。
二、垃圾收集算法
1. 标记-清除算法
最早出现也是最基础的垃圾收集算法,后续所有的垃圾收集算法都是基于它的,是用于在老年代的垃圾回收算法,常见如CMS垃圾回收器(低延迟)。
算法原理:
- 标记,标记出所有需要回收的对象;
- 清除,统一回收掉所有标记的对象。
代价:对大量可回收对象时执行效率低;会产生大量的内存空间碎片。
2. 复制算法
主要是为了解决标记清除算法对大量可回收对象执行效率低的问题,它适合用于新时代收集算法(对象朝生夕死特性)。
算法原理:
- 将可用内存按容量划分为大小相等的2块;
- 将还存活对象复制到另一个空间上;
- 将之前使用过的内存空间一次清理掉
代价:空间浪费,将可用的空间缩小为原来一半;如果存活对象比较多的或比较大,复制效率就比较低。
3. 标记-整理算法
主要解决标记清除算法内存碎片化的问题,它适合老年代对象回收特性(长期存活对象),如Parallel Old垃圾收集器(吞吐量)。
算法原理:
- 标记,标记需要回收对象;
- 整理,让存活对象都向内存另一端移动;
- 清理,直接清理边界以外的内存.
代价:收集花费时间相对于标记清除算法会慢一些。
三、并发的可达性分析
1. 作用
根据 可达性分析算法 的核心概念,利用系列根对象(GC Roots )作为起始点,根据对象之间的引用关系搜索出一条引用链,通过遍历引用链来判断对象的是否存活。
在这个过程中,根对象枚举的时间非常短暂且相对固定,然而,遍历所有引用链(对象图)所需要的时间与对象的数量成正比。因此,对象越多,对象之间的引用关系就越复杂,需要更多的时间去遍历所有的引用链来标记所有的对象。
为了解决以上问题就有了并发标记,它的作用就是让垃圾回收线程和用户线程能够同时进行,并发执行。具体看下面的三色标记。
2. 三色标记
- 白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是 白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。
- 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。黑色的对象代 表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对 象不可能直接(不经过灰色对象)指向某个白色对象。
- 灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过,也就是还可以继续扫描。
三色标记问题
当且仅当以下两个条件同时满足时,会产生“对象消失”的问 题,即原本应该是黑色的对象被误标为白色:
- 插入了一条或多条从黑色对象到白色对象的新引用;
- 删除了全部从灰色对象到该白色对象的直接或间接引用。
解决方案
增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新 插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫 描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象 了。
原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删 除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描 一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来 进行搜索。