本文介绍了PhantomReference的作用,以及它与WeakReference区别。同时纠正网上关于引用对象回收时间的错误说法(个人认为不正确)。本文没有考虑finalize方法是如何实现(具体可以参考Finalizer和FinalReference的介绍),只考虑finalize方法执行条件和结果。
下面介绍一个phantom reference 引用的对象从创建到被回收会经历哪些事
public static TestObject instanceA = null;
ReferenceQueue<TestObject> queue = new ReferenceQueue<>();
instanceA = new TestObject(); // 创建对象
PhantomReference<TestObject> phantomRef =
new PhantomReference<TestObject>(instanceA, queue); //创建phantom引用
instanceA = null; // 对象已经不需要再使用了,可以被回收了
// GC发现了这个对象可以被回收,如果对象覆盖了finalize方法还需要执行finalize方法
System.gc();
// 对象的finalize方法
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed");
}
这里解释下为什么要两次GC才能将对象视为phantom可达对象。因为第一次GC时,finalize方法没有被执行过,所以即使对象已经没有其他引用了,但是仍然不满足phantom可达对象的条件(对象处于finalized状态)。如果对象没有覆盖finalize方法就可以直接视为finalized状态,那么一次GC就可以将对象视为phantom可达对象。第二次GC发现对象没有其他引用,同时又处于finalized状态,那么这次就可以将对象视为phantom可达对象。
如果第二次GC发现对象有其他引用呢?这里就引申出对象再生问题,为什么对象会再生呢?这种情况只可能发生在finalize方法中将对象赋值给了其他引用(不推荐这种用法),因为能访问到对象的只有其本身(PhantomReference的get方法也访问不到对象,虽然反射可以得到对象的引用但是这不是个好习惯)。如果已经发生了这种意外怎么办?本来是希望让finalize方法作为对象回收时的清理工作(期望对象被回收),但是对象却再生了,很显然不能期望在 finalize 方法中进行资源释放
为了应对上面finalize的缺陷,phantom reference 派上了用场。当然这也不是单靠phantom reference就可以完成的,它也利用了finalize方法只能执行一次的特点,也就是说对象只可能再生一次。那么当GC再次发现对象不可达之后,它之前就处于finalized状态了,这时就满足了phantom可达对象的条件。然后GC就会将phantom reference 添加到pending链表,以便ReferenceHandler将其放入队列。
网上有人认为对象有几种状态Unfinalized Finalizable Finalized Reclaimed,那么这几种分别对应上面对象的哪个生命阶段呢?
网上有人说weak reference要等到对象被释放后才能入队,而phantom reference在对象被释放之前就入队了,这个说法有没有问题?
public class TestObject{
public static TestObject instanceA = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed");
}
public static void main(String[] args)
throws InterruptedException {
ReferenceQueue<TestObject> queue = new ReferenceQueue<>();
instanceA = new TestObject(); //创建对象
/*WeakReference<TestObject> weakRef =
new WeakReference<TestObject>(instanceA, queue); */
PhantomReference<TestObject> phantomRef =
new PhantomReference<TestObject>(instanceA, queue); //创建phantom引用
instanceA = null; // 不再使用对象
System.gc();
/*
* 将pending链表节点加入queue的ReferenceHandler是并发执行的,
* 等待其将reference放入queue。另外,执行finalize方法的
* FinalizerThread线程优先级很低,需要等finalize方法执行。
*/
Thread.sleep(1000);
System.gc();
System.out.println(queue.poll());
}
}