第四篇内容,我们分享了如何判定一个对象是否存活,以及对象的二次逃脱,本节就在上一篇的基础上,讲解内存的回收算法。
回收算法
1. 标记——清楚算法
算法思路:
分为“标记”和“清除”两个阶段:首先标记处所有需要回收的对象,在标记万恒后统一回收被标记的对象
缺点:
a. 效率低
b. 会产生大量内存碎片
2、复制算法
算法思路:
它将可用内存按容量划分为大小相等的两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉。
这样每次只需要对整个半区进行内存回收,内存分配时也不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按照顺序分配即可。复制算法的执行过程如图:
缺点:
a. 效率也比较低,比如在对象存活率较高的场景下要进行大量的复制操作,当然如果极端情况,比如对象100%存活的情况下,还需要额外的空间进行分配担保
b. 耗费内存空间
3、标记-整理算法
算法思路:
根据老年代的特点,有人提出了另外一种标记-整理算法,过程与标记-清除算法一样,不过不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。标记-整理算法的工作过程如图:
4、分代收集算法
算法思路:
作为现代商用虚拟机基本都采用的“算法”,与其称之为一种算法,不如说是上面三种算法的一种组合。即,Java堆一般分为新生代和老年代,根据各个年代的特点,分别采用上面三种算法的某一种来进行垃圾回收
比如,在新生代中,每次垃圾回收都发现有大量的对象死去,只有少量存活,这种情况,采用复制算法效果会更好一点,只需要复制少量存活的对象就可以完成收集。
而老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记——清除”或“标记——整理”算法来进行回收。
堆内存布局如下图所示: