Reference类是所有引用对象(包括WeakReference、SoftReference、PhantomReference等)的抽象基础类。这个类定义了所有引用对象的公共操作。因为引用对象的实现是跟垃圾收集器紧密关联的,所以这个类不应该被直接继承
ReferenceQueue,引用对象在合适的可达性状态(Reference指向的对象不存在任何强引用)被探测到时,由垃圾收集器将其添加到所注册的那个ReferenceQueue中
我想只要说清楚这两个类的设计上的两个关键点就足够了解它们了
1.如何实现队列
ReferenceQueue是一个先进后出(FILO)的队列,用于保存注册在这个队列的且引用的对象已经被垃圾收集器回收的引用对象。这个队列的实现其实是由这两个类的协同完成的。
ReferenceQueue类中保存一个Reference对象的属性head,用于表示队列的队顶。
每个Reference对象都有一个Reference对象的属性next,用于保存队列中下一个元素。可以看看一个Reference对象被放入ReferenceQueue时做了什么
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ synchronized (lock) { // Check that since getting the lock this reference hasn't already been // enqueued (and even then removed) ReferenceQueue<?> queue = r.queue; if ((queue == NULL) || (queue == ENQUEUED)) { return false; } assert queue == this; r.queue = ENQUEUED; //关键在这,队列的head赋值给新加入队列的Reference对象的next属性,如果队列 head为空,next就赋值为Reference对象自己。然后再把新加入对象赋值给队列的head r.next = (head == null) ? r : head; head = r; queueLength++; if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(1); } lock.notifyAll(); return true; } }
2.Reference是如何被放入队列的
Reference类有一个内部静态类ReferenceHandler extends Thread,这个内部类中有静态代码块生成该类的一个守护线程对象并启动。那这个静态内部类的run方法做了什么,有什么作用呢?
run方法代码如下
public void run() { while (true) { tryHandlePending(true); } }
然后把tryHandlePending的关键代码贴出来
if (pending != null) { //取出pending对象 r = pending; // 'instanceof' might throw OutOfMemoryError sometimes // so do this before un-linking 'r' from the 'pending' chain... c = r instanceof Cleaner ? (Cleaner) r : null; // unlink 'r' from 'pending' chain //把pending对象的discovered属性设为pending pending = r.discovered; r.discovered = null; } else { // The waiting on the lock may cause an OutOfMemoryError // because it may try to allocate exception objects. if (waitForNotify) { lock.wait(); } // retry if waited return waitForNotify; }
它的作用就是不停地从pending队列取出,并放入ReferenceQueue中。
这里又出现一个pending队列,它的实现也很简单。Reference类中有一个静态属性Reference pending。这个pending对象相当于pending队列的顶部,它的discovered属性则是pending队列的下一个元素,以此类推。
那又是谁把这些Reference对象放入pending队列呢?答案是垃圾收集器,它会把可达性合适的引用放入pending队列
那么整个过程就很明了了,一旦一个Reference引用指向的对象可达性合适,这个引用就会被垃圾收集器放到pending队列中,而同时一个辅助线程ReferenceHandler则不停地从中取出Reference对象放入到该对象注册的ReferenceQueue中