在堆内存中存放着 Java 程序中几乎所有的对象实例,堆内存的容量是有限的,Java 虚拟机会对堆内存进行管理,回收已经 "死去" 的对象(即不可能再被任何途径使用的对象),释放内存。垃圾收集器在对堆内存进行回收前,首先要做的第一件事就是确定这些对象中哪些还存活着,哪些已经死去。Java 虚拟机是如何判断对象是否可以被回收的呢?
引用计数算法
引用计数算法的原理是这样的:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加 1;当引用失效时,计数器值就减 1;在任何时刻计数器的值为 0 的对象就是不可能再被使用的,也就是可被回收的对象。
引用计数算法的效率很高,但是主流的 JVM 并没有选用这种算法来判定可回收对象,因为它有一个致命的缺陷,那就是它无法解决对象之间相互循环引用的的问题,对于循环引用的对象它无法进行回收。
假设有这样一段代码:
- public class Object {
- public Object instance;
- public static void main(String[] args) {
- // 1
- Object objectA = new Object();
- Object objectB = new Object();
- // 2
- objectA.instance = objectB;
- objectB.instance = objectA;
- // 3
- objectA = null;
- objectB = null;
- }
程序启动后,objectA 和 objectB 两个对象被创建并在堆中分配内存,这两个对象都相互持有对方的引用,除此之外,这两个对象再无任何其他引用,实际上这两个对象已经不可能再被访问(引用被置空,无法访问),但是它们因为相互引用着对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 收集器回收它们。
实际上,当第 1 步执行时,两个对象的引用计数器值都为 1;当第 2 步执行时,两个对象的引用计数器都为 2;当第 3 步执行时,二者都清为空值,引用计数器值都变为 1。根据引用计数算法的思想,值不为 0 的对象被认为是存活的,不会被回收;而事实上这两个对象已经不可能再被访问了,应该被回收。
可达性分析算法
在主流的 JVM 实现中,都是通过可达性分析算法来判定对象是否存活的。可达性分析算法的基本思想是:通过一系列被称为 "GC Roots" 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到 GC Roots 对象没有任何引用链相连,就认为 GC Roots 到这个对象是不可达的,判定此对象为不可用对象,可以被回收。
在上图中,objectA、objectB、objectC 是可达的,不会被回收;objectD、objectE 虽然有关联,但是它们到 GC Roots 是不可达的,所以它们将会被判定为是可回收的对象。
在 Java 中,可作为 GC Roots 的对象包括下面几种:
1、虚拟机栈中引用的对象;
2、方法区中类静态属性引用的对象;
3、方法区中常量引用的对象;
4、本地方法栈中 Native 方法引用的对象。
以上探讨了判定对象是否可回收的两种算法,判定对象是否可回收只是垃圾回收的第一步,接下来还要解决何时回收以及如何回收的问题,在后面的文章中我们来探讨这些问题。
来源: http://www.cnblogs.com/fangfuhai/p/7197750.html