1引用计数算法
当有地方引用这个对象的时候,引用计数器的值+1,当引用失效的时候,计数器的值-1
很难解决对象之间相互循环引用的问题
2可达性分析算法
从根(GC Roots)的对象作为起始点,开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连(用图论的概念来讲,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
一般引用计数和可达性分析 一起使用才能较为完整的完成 gc的过程
3根搜索算法
由于引用计数法的缺陷 所以采用了一种新的算法
设立若干个跟对象当一个根对象到到某个跟对象都是不可达的时候,就认为这个对象是可以被回收的
4垃圾搜集的算法主要有三种,分别是标记-清除算法、复制算法、标记-整理算法。这三种算法都扩充了根搜索算法,不过它们理解起来还是非常好理解的。
(1)标记-清除算法
分为两个阶段 标记阶段 清除阶段
标记 :首首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;
清除: 在清除阶段,清除所有未被标记的对象
详解:
标记:标记的过程其实就是,遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。
清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。
效率比较低需要遍历堆中所有的对象
(2)复制算法
将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
与标记-清除算法相比,复制算法是一种相对高效的回收方法
不适用于存活对象较多的场合,如老年代(复制算法适合做新生代的GC)
(3)标记-整理算法
标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记;但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间。
标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。
整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段
标记-清除算法、复制算法、标记整理算法的总结:
三个算法都基于根搜索算法去判断一个对象是否应该被回收,而支撑根搜索算法可以正常工作的理论依据,就是语法中变量作用域的相关内容。因此,要想防止内存泄露,最根本的办法就是掌握好变量作用域,而不应该使用C/C++式内存管理方式。
在GC线程开启时,或者说GC过程开始时,它们都要暂停应用程序(stop the world)。
它们的区别如下:(>表示前者要优于后者,=表示两者效果一样)
(1)效率:复制算法>标记/整理算法>标记/清除算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
(2)内存整齐度:复制算法=标记/整理算法>标记/清除算法。
(3)内存利用率:标记/整理算法=标记/清除算法>复制算法。
注1:可以看到标记/清除算法是比较落后的算法了,但是后两种算法却是在此基础上建立的。
注2:时间与空间不可兼得
5分代收集算法:
少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。
大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。