深入理解Java虚拟机——引用计数法和可达性分析法

534 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

垃圾收集器在进行垃圾回收前需要确定哪些对象是存活的,目前主流有两种算法:引用计数法和可达性分析法。

引用计数法

为每一个对象添加一个引用技术器,每有一处引用时计数器的值加一,当引用失效时计数器的值减一,当计数器为0则表示对象不会再被使用。引用计数法在主流的Java虚拟机中没有选用,主要是由于难以解决对象之间循环引用的问题。 以下面代码为例,对象objA和objB进行了循环引用,导致它们的引用技术都不为零,引用计数法无法回收它们。

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(); 
    }

}

可达性分析法

主流的程序语言采用的对象存活算法主要是可达性分析算法,基本思路是以GC Roots作为起始节点根据引用关系向下搜索,将路径形成一条引用链,不在链上的对象均为可回收对象。

image.png

可以被作为GC Roots的对象有一下几种

  1. 虚拟机栈中引用的对象,主要是栈帧中的本地变量表,这里存有方法的参数、局部变量等。
  2. 在本地方法栈中引用的对象。
  3. 方法区中类静态属性引用的对象,主要是Java类的引用类型静态变量。
  4. 方法区中常量引用对象,主要是对于字符串常量池的引用。
  5. 被同步锁synchronized持有的对象。
  6. 虚拟机内部的引用,例如基本数据类型对应的Class对象(Integer、Charactor等)、异常对象(NullPointException、OutOfMemoryError)、系统类加载器。
  7. 反映虚拟机内部情况的JMXBean、本地代码缓存等。