Java虚拟机:如何判定哪些对象可回收?

Posted 风中程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java虚拟机:如何判定哪些对象可回收?相关的知识,希望对你有一定的参考价值。

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!

       在堆内存中存放着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方法引用的对象。

       以上探讨了判定对象是否可回收的两种算法,判定对象是否可回收只是垃圾回收的第一步,接下来还要解决何时回收以及如何回收的问题,在后面的文章中我们来探讨这些问题。

 

以上是关于Java虚拟机:如何判定哪些对象可回收?的主要内容,如果未能解决你的问题,请参考以下文章

java虚拟机——垃圾回收

4 Java 如何判定是否存活或者死亡

避免使用finalize方法

垃圾回收

Java虚拟机

Java虚拟机二:垃圾回收机制