从如何判定对象消亡的角度,垃圾收集算法可以划分为“引用计数式垃圾收集”(reference counting gc)和“追踪式垃圾收集”(tracing gc)两大类,这两类也常被称为“直接垃圾收集”和“间接垃圾收集”,现在主流的java虚拟机都采用的是追踪式垃圾收集算法。追踪式垃圾回收算法的策略并非是寻找垃圾本身,而是先寻找哪些对象存活,然后反过来判断其余所有的对象为垃圾对象。追踪式回收算法包括标记-清除(mark-sweep)算法、标记-复制(mark-copy)算法、标记-整理(mark-compact)算法。
一.标记-清除算法:
1.原理:标记-清除算法是最早出现也是最基础的垃圾收集算法算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收未标记的对象。标记的过程就是对象是否属于垃圾的判定过程。
2.缺点:
(1)执行效率不稳定。如果java堆中包含大量对象,而且其中大部分是需要回收的,这时必须进行进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低。
(2)内存空间的碎片问题。标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
二.标记-复制算法(复制算法):
对标记-清除算法的改进引出了复制算法。
1.原理:标记-复制算法常被简称为复制算法。所谓复制算法,就是把内存分为2块等同大小的内存空间(a和b),使用a进行内存的使用,当a部分的内存不足以分配对象而引起内存回收时,就会把存活的对象从a内存块放到b内存块中,后把a内存块中的对象全部清除,在b内存块中使用,当b内存不足以分配内存时,就会把b中存活的对象放到a内存块中,然后把b中对象全部清除,如此循环。
使用这种方式可以避免出现空间碎片(内存中不连续的空间)。
2.缺点:
浪费了一半的内存,降低空间的使用率。
三.标记-整理算法:
由于前两种都有问题,提出了第三种垃圾收集算法。
1.原理:标记-整理算法的标记过程与“标记-清理算法”一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
整理后的内存,避免了标记清除中的内存碎片的问题,也避免了复制算法中的内存浪费问题,当然,存在的问题就是效率问题了,会比前2者的效率低。
总结:
当前商业虚拟机的垃圾收集器,大多数都遵循“分代收集”的理论进行设计的。设计者一般至少会把java堆划分为新生代和老年代两个区域。在新生代中每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。
年轻代是采用复制算法进行垃圾回收,因为年轻代一般都是存活时间不长的对象,在第一次进行垃圾回收的时候,会把大部分的对象清除掉,这种情况下使用复制算法,只需要把少量存活的对象放入到另一块闲置的内存块中即可。
而老年代中,一般对象的存活比例会很高,这种情况下,使用复制算法不能很好的提高性能和效率,把存活的对象移到另一个内存块时,会由于对象存活多而消耗的时间多,从而影响效率,这种情况下,使用标记整理或者标记清除比较合适。