3.3垃圾收集算法

一、标记—清除算法

标记—清除算法是最基础的收集算法,过程分为标记和清除两个阶段,首先标记出需要回收的对象,之后由虚拟机统一回收已标记的对象。这种算法的主要不足有两个:
1、效率问题,标记和清除的效率都不高
2、空间问题,对象被回收之后会产生大量不连续的内存碎片,当需要分配较大对象时,由于找不到合适的空闲内存而不得不再次触发垃圾回收动作

3.3垃圾收集算法_第1张图片
image.png

二、复制算法(主要用于回收新生代)

为了解决效率问题,复制算法出现了。算法的基本思路是:将内存划分为大小相等的两部分,每次只使用其中一半,当第一块内存用完了,就把存活的对象复制到另一块内存上,然后清除剩余可回收的对象,这样就解决了内存碎片问题。我们只需要移动堆顶指针,按顺序分配内存即可,简单高效。但是算法的缺点也很明显:
1、它浪费了一半的内存,这太要命了。
2、如果对象的存活率很高,我们可以极端一点,假设是100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍。复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变的不可忽视。

所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%内存的浪费。


3.3垃圾收集算法_第2张图片
image.png

三、标记—整理算法(可用于老年代回收)

根据老年代的特点,有人提出了另一种改进后的“标记—清除”算法:标记—整理算法。
标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。
整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

3.3垃圾收集算法_第3张图片
image.png

可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。

不难看出,标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价,可谓是一举两得。

四、分代收集算法

现代商业虚拟机垃圾收集大多采用分代收集算法。主要思路是根据对象存活生命周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,然后根据各个年代的特点采用最合适的收集算法。新生代中,对象的存活率比较低,所以选用复制算法,老年代中对象存活率高且没有额外空间对它进行分配担保,所以使用“标记-清除”或“标记-整理”算法进行回收


参考:
JVM内存管理------GC算法精解(复制算法与标记/整理算法)

你可能感兴趣的:(3.3垃圾收集算法)