三种基础的垃圾收集算法,虚拟机内存的划分也是按照对象的生存特征进行分而治之,那么不同区域的回收也对应不同的算法来提高回收的效率,当然也有算法之间进行配合使用的,这三种回收算法是所有算法的基础并在此基础上进行扩展。

  • 标记-清除
  • 标记-复制
  • 标记-整理

    标记-清除

    从名字上可以看出此算法分为两个部分 标记、清除,标记就是通过可达性分析之后找出不需要使用的对象进行标记等到所有需要回收的对象标记完成后把这些对象清除掉,也可以反过来标记那些存活的对象,然后直接清除掉未被标记的对象。后续其他的算法也是基于此算法进行升级改造的,它主要有两个缺点:
  1. 如果内存中大部分对象都是需要被回收的,那个它需要把这些对象全部标记出来,这个会造成标记和清除的过程的时间会很长,而且随着对象的数量的越多时间就越不确定性能越低。
  2. 在被扫描的区域中哪些被标记清除的对象的位置可能比较散落,回收后的空间也就相对比较零散不连续也叫空间碎片化,这会造成我们在给一些大对象分配连续内存空间时内存不足而造成提前触发一次垃圾回收。

    标记-复制

    为了解决标记清除算法在标记回收过程中性能低下和空间碎片的问题,标记复制算法提出了一个解决方案就是把一块内存一分为二,每一次只使用其中一块等到内存快满了的时候把标记的存活对象复制到另外一块没有使用的区域中,然后把当前区域要回收的对象全部清理掉,如果极端的情况下大部分对象都存活那么就会产生大量的复制开销,但对于大部分对象都是可回收的情况下,只复制少量存活的对象效率也是很高的而且只回收半个区域也没有空间碎片的问题分配对象只需要移动指针按照顺序分配即可,但是这个算法的缺点就是总会有一半的内存在空闲着着实有些浪费空间,现在的大部分虚拟机的新生代都采用这种算法但对此进行了优化。

  3. 将空间分为一个较大的Eden空间和两个较小的Survivor空间,每次只分配Eden空间和其中一块Survivor空间,发生垃圾收集时将存活下来的对象复制到另一块Survivor空间(空间比是8:1,80%的Eden空间加上一个10%的Survivor空间,只浪费了10%的空间用于存放存活的对象)。

  4. 如果10%的对象存活空间满了,jvm还设计了逃生门,如果经过一次MinorGc后空间不足以存放存活的对象就需要依赖于其他区域内存进行担保。

    标记-整理

    标记整理算法是通过标记存活的对象然后将存活对象向指定区域移动然后把这块区域以外的对象清除掉,有点类似于标记清除算法,区别在于标记清除算法是非移动的而标记整理算法是移动的,移动就意味着需要更新所有存活下来的对象的引用地址,这个过程是需要Stop The World 极为负重的操作。
    要是像标记清除算法那样不去移动对象那么它就会存在空间碎片化的问题,没有整齐的内存那么我们在分配对象的时候就需要依赖于更为复杂的分配对象的方式比如常见的分区空闲列表的方式也就是在内存中的数据存放的地址就不是连续的,这样会给对象访问造成一定的效率问题从而影响应用程序的吞吐量。
    综上分析可以知道移动或者不移动对象都存在弊端,移动对象在回收时候会变得复杂,不移动对象则在分配的时候会变得复杂,从垃圾回收的停顿时间的角度看似乎不移动对象更划算,但是从整个应用的吞吐量来说似乎移动对象更加划算。像CMS垃圾收集器采用了这两种方式的结合,先通过标记清除来回收对象减少stw的时间,等到碎片影响到对象的分配时在通过标记整理算法来解决空间碎片化的问题。