判断一个对象是否为垃圾的算法主要有如下这两种:
- 引用计数算法
- 可达性分析算法
下面我们来分别看一下。
引用计数算法
为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。
优点:
- 实现简单
- 执行效率高,对程序的执行影响较小
引用计数算法在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,比如使用 ActionScript3 的 FlashPlayer、Python 语言都使用了引用计数算法来进行内存管理。
但是引用计数算法的缺点也非常的明显,它很难解决对象之间相互循环引用的问题,所以主流的 Java 虚拟机里面没有选用引用计数算法来管理内存。
循环引用的例子:
public class ReferenceCounterProblem{
public ReferenceCounterProblem instance = null;
public static void main(String[] args) {
ReferenceCounterProblem a = new ReferenceCounterProblem();
ReferenceCounterProblem b = new ReferenceCounterProblem();
a.instance = b;
b.instance = a;
a = null;
b = null;
}
}
这种情况下,原来 a 和 b 所指向的对象都无法再访问了(因为 a 和 b 均置为 null 了),但是,使用引用计数算法的话,a 和 b 所指向的对象,此时的引用计数器均为1,且不会再改变了,所以这两个对象不会被回收,此时内存泄露发生。
可达性分析算法
在主流的商用程序语言(包括 Java、C# 等)的主流实现中,都是通过称为可达性分析算法来判定对象是否存活的。
这个算法的基本思想就是 通过一系列的称为 ”GC Roots“ 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可被回收的。 相反,可达的对象都是存活的。
如下图
Object 1 ~ 4 均判定为存活的对象,而 Object 5 ~ 7 均判定为可回收的。
可以作为 GC Roots 的对象主要有如下四种:
- 虚拟机栈中局部变量表中引用的对象
- 本地方法栈中 JNI (即 API 中那些 native 方法)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中的常量引用的对象