标记-清除算法
-
分为“标记”和“清除”两个阶段
- 标记阶段:
标记的过程其实就是前面介绍的可达性分析算法的过程,遍历所有的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象 - 清除阶段:
清除的过程是对堆内存进行遍历,如果发现某个对象没有被标记为可达对象(通过读取对象header信息),则将其回收
- 标记阶段:
-
不足
-
标记和清除效率都不高
-
标记、清除之后会产生大量不连续的内存碎片
-
复制算法(新生代)
对象存活率低,且有老年代分配担保,因此常采用复制算法
-
Eden:Survivor:Survivor=8:1:1
-
分配担保
如果Survivor没有足够空间来存放上次新生代GC存活下来的对象,它们将通过分配担保机制进入老年代
-
不足
-
将内存缩小为原来的一半,浪费了一半的内存空间,代价太高;如果不想浪费一半的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法
-
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低
-
标记-整理算法(老年代)
对象存活率高,且没有额外空间对它进行分配担保,因此常用标记清除或标记整理算法来实现
- 先执行标记-清除,让所有的存活对象都向一端移动,最后清理掉边界以外的内存
- 不足
- 效率不高,不仅要标记存活对象,还要整理所有存活对象的引用地址,在效率上不如复制算法
分代收集算法
复制算法和标记整理法的结合
标记-压缩算法
标记阶段和之前的标记-清除中提到的标记阶段完全一样,然后我们就通过遍历数次堆来进行压缩。这里的压缩指的就是复制-清除里面的把存活对象重新装填,使对象都紧挨在一起,从而避免内存碎片的产生,同时保证内存的高速分配。不过与GC复制算法不同的是,标记-压缩不需要牺牲额外的空间