概述

本文只讨论算法思想,不会具体到某种语言的实现方式以及额外细节。

可达性分析就是通过从某些对象为root节点开始对其引用链进行遍历,通常无法被遍历到的对象则认为是需要回收的对象。

举栗子

例子一

class A {
  public B ref;
}

class B {
  public A ref;
}

class C {
  public D ref;
    D(D ref) {
      this.ref = ref;
    }
}
public void Foo() {
  A a=  new A();
  B b = new B();
  C c = new C(new D());
}

当上面的三行代码执行完毕后,引用链是下图这样的(此时还没有销毁局部变量)

图片加载失败

如果继续执行这个代码c = null;,则引用链变化为这样

图片加载失败

此时我们发现Object CObject D已经可以回收了。因为我们发现通过变量abc所指向的对象均不能沿着引用链访问到这两个对象。

当栈帧关闭后,局部变量被销毁,对于上述代码,所有的对象均可回收。

例子二

class E {
  public static C c;
  public E() {
    C c0 = new C(new D());
    c = c0;
    c0 = null;
  }
}

当对象E的构造函数内的三行代码执行完毕后,引用链是这样的(此时局部变量还没销毁)

图片加载失败

此时虽然栈区中没有变量可以沿着引用链访问到这两个对象,但是在静态区中还是有一个变量可以的,此时这两个对象是不能回收的。

哪些对象可以作为root节点

从例子一中可以看出局部变量指向的对象是需要作为root节点。从例子二中看出存储在静态区的变量也是要作为root节点。

当然这些只是理论上的,实际上在各种带有GC的语言都有些许不同,但是思想都是一样的。

优点

从实现思想上来说这种方法可以延迟执行,也有就说GC可以选择在一个合适的时机来检测垃圾对象。同时从例子一可以看出来,可达性分析解决了引用计数无法处理的循环引用问题。

缺点

  • 沿着引用链的遍历过程相对于引用计数的数字增减开销较大。
  • 不适用与一些内存资源紧张的环境,因为可达性分析是需要延迟回收的,向引用计数一样频繁执行性能开销无法接受。