标记-清除算法
正如它的名字一样,它分为两个操作:标记、清除。
从根节点GC ROOT开始往下找,当一个对象脱离了根节点的时候,也就是这个对象没有一条可以到达根节点的路径,我们就将该对象标记为一个垃圾对象,就如下图中的B和E对象。
标记:
再标记完成之后,就开始执行清除的操作了。
这种方式的缺点:
- 需要进行两个操作,标记和清除,效率低;
- 在清除对象之后,会导致内存空间不连续的问题,如上图所示,假设现在需要存放一个更大的对象,本来两个格子的空间是够的,但是这些空间不连续,一个格子又存放不下这个对象。
复制算法
这种算法的原理是采用了两块内存空间,或者可以说是将一块内存空间分为了两块。但是实际上我们还是使用一块内存空间。
- 首先还是先从我们的GC ROOT根节点开始往下找,先标记出需要回收的垃圾对象
- 将存活的对象复制到另一块内存空间中
- 然后再把原内存空间中的内容一次性清理掉
缺点:
- 比其它方式多占用一块内存空间;
- 需要回收复制移动对象。
现在很多商业的虚拟机都是采用这个方式来回收新生代的。 因为新生代中存放的对象大多数都是存活时间很小的,也就是说存活下来的对象很少(98%都会被垃圾回收),所以在复制移动的时候不需要对大量的对象进行操作。
也可以看我的另一篇文章: 分代回收算法之新生代垃圾回收流程https://blog.csdn.net/sunao1106/article/details/126693446?spm=1001.2014.3001.5502
标记-整理算法
在上面也提到了,复制算法适用于新生代,但是不适用于老年代,因为在老年代中,大部分对象都是存活率比较高的,如果使用复制算法,将会执行多次的复制操作,而且要多分配一个内存空间出来。
标记-整理算法的第一步也是先对垃圾对象进行标记,和标记-清除法一样,但是下一步并不是将标记的垃圾对象直接进行清除,而是将存活的对象收集到一片连续的区域,也就是将它们都靠在一起,然后在清除标记的垃圾对象。
标记:
将存活的对象像一端移动:
删除标记的垃圾对象:
分代收集法
在当前的商业虚拟机中都是采用这个方法来进行垃圾回收的,它是根据对象的存活周期去划分不同的内存区域,比如,在JVM中,将堆分为新生代和老年代。
然后再根据这些代的特点,采用不同的算法(上述讲的三种)进行垃圾回收。比如在新生代中,对象的存活率比较低,就会采用复制算法;在老年代中,对象的存活率较高,就会采用标记-清除算法或者标记-整理算法。
在新生代中又分为Eden区、survivor from和survivor to,这样做的目的就是为了去使用复制算法进行垃圾回收。