您的位置:首页 > 编程语言 > Java开发

JVM中判断对象是否存活的方法

2016-10-24 15:51 309 查看
  Java中几乎所有的对象实例都存放在堆中,在垃圾收集器对堆内存进行回收前,第一件事情就是要确定哪些对象还“存活”,哪些对象已经“死去”(即不可能再通过任何途径被使用)。

引用计数算法

  首先需要声明,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存。

  什么是引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1.任何时刻计数器值为0的对象就是不可能再被使用的。那为什么主流的Java虚拟机里面都没有选用这种算法呢?其中最主要的原因是它很难解决对象之间相互循环引用的问题。在下面的代码中,我们在testGC()中new两个ReferenceCountingGC对象objA和objB,然后令objA.instance=objB,objB.instance=objA,最后令objA和objB都为null。此时,这两个对象实际上已经不可能再被访问,但由于它们互相引用着对方,导致它们的引用计数器值都不为0,于是使用引用计数算法无法通知GC收集器回收它们。

  public class ReferenceCountingGC {

public Object instance = null;

private static final int _1MB = 1024 * 1024;

/**
* 这个成员属性的唯一意义就是占点内存,以便在能在GC日志中看清楚是否有回收过
*/
private byte[] bigSize = new byte[2 * _1MB];

public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;

objA = null;
objB = null;

// 假设在这行发生GC,objA和objB是否能被回收?
System.gc();
}

public static void main(String[] args) {
testGC();
}
}


  现在,我们在main方法中调用testGC(),然后打印GC日志(在Myeclipse中打印GC日志的方法)。得到结果为:

0.161: [GC 4761K->568K(124416K), 0.0022505 secs]

0.163: [Full GC 568K->471K(124416K), 0.0201927 secs]

  由“4761K->568K”我们可以得知,虚拟机并没有因为这两个对象相互引用就不回收它们,这也从侧面说明了hotspot虚拟机并不是通过引用计数算法来判断对象是否存活的

那么,在主流的商用程序语言的主流实现中,是通过什么方法来判断对象是否存活的呢?答案是下面将要介绍的可达性分析算法。

可达性分析算法

  可达性分析算法的基本思路是:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点考试向下探索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如下图中,对象object5、object6、object7虽然互有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。

  


  

在Java语言中,可作为GC Roots的对象包括下面几种:

1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;

2. 方法区中类静态属性引用的对象;

3. 方法区中常量引用的对象;

4. 本地方法栈中JNI(即一般说的Native方法)引用的对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm java GC