(1)引用计数法
这是最经典和最古老的一种,它的工作原理是为每个对象配备一个计数器,只有这个对象被引用了,引用计数器就加1,当引用失效时,计数器减一,只要计数器为0,意味着不再被使用,于是该对象就可以被回收了。
优点:实现简单;
缺点:无法处理循环引用情况。即有应该被回收的垃圾对象A、B,他们互相引用,除此之外无其他引用,然而他们计数器值不是0,因此无法回收,从而造成内存泄漏。
(2)标记-清除算法
这个算法将垃圾回收分为两个阶段,标记阶段和清除阶段,在标记阶段,首先通过根结点,标记所有从根结点开始的可达对象,因此未被标记的对象就是未被引用的垃圾对象,即使存在上面那样的相互引用的垃圾对象,但是由于从根结点出发无法到达,因此他们也未被标记。然后,在清除阶段,清除所有未被标记的对象。
优点:处理了循环引用的问题;
缺点:回收后的空间往往是不连续的,容易产生空间碎片。如此,在对象的堆空间分配时,尤其是大对象,不连续的内存空间的工作效率是明显比连续的低的。
(3)复制算法
算法思想是把原来的可用的内存等分为两块,然后每次只是使用其中一块,在垃圾回收的时候,就把正在使用的那块内存里的存活的对象移动到另一块未被使用的内存中,然后清除原来那块,再把存活对象整体移动过来,如此就克服了空间不连续的缺点。
在Java的堆新生代串行垃圾回收器中就应用了该算法,新生代分为eden空间、from空间和to空间这3个部分。eden空间充当每次被使用的空间,而from和to空间就是移动的缓存空间。
优点:适用于新生代,因为新生代中垃圾对象往往多余存活对象,因此复制算法效果好些。
缺点:虽然比标记清除算法高效些,然后,系统内存折半使用,终究是一大缺陷。
(4)标记-压缩算法
老年代是不太适合用复制算法的,因为大部分都是存活对象,复制成本高。因此,在标记-清除算法基础上改进,标记阶段一样,而在清除阶段,把所有存活的对象压缩驱赶到内存的一端,使得它连续起来。然后再清除其他空间。如此就避免了碎片空间的产生。
(5)增量算法
对于大部分垃圾回收算法而言,由于垃圾回收时,除了垃圾回收线程外,进程其他线程都会高高挂起,直等到垃圾回收完成。然而,如果,你垃圾回收很久还没结束,用户就会等得不耐烦了。例如,在Android应用中,如果界面响应时间超过5s还没有反应,那就出现ANR,系统提示要不要停止该应用了。因此,为了避免影响用户体验,一次性完成所有垃圾的清理不太明智的,增量算法的思想则是让垃圾回收线程与其他应用线程交替执行,每次垃圾回收线程只是清理一小块内存。如此,减少系统停顿时间。
缺点是:因为线程切换和上下文切换,垃圾回收成本上升了,造成系统吞吐量下降。
总结:以上5种算法各有优劣,只在JVM中用一种显然是不好的, 最好的办法是对垃圾回收器分年代,分为新生代和老年代。一个对象新创建的时候进入到新生代,然后垃圾回收几次后,该对象依然存活,于是就放入老年代。新生代多用复制算法,老年代多用标机压缩算法。然后整个垃圾回收器的工作线程是用增量算法,时间轮转片执行。此谓之分代思想!
原文:http://blog.csdn.net/linfeng24/article/details/38348969