Reference是个抽象类,它实现了所有reference对象的通用操作。这个类主要跟垃圾收集器密切相关。
1.Reference内部四种状态
- Active:Reference对象被垃圾收集器特殊对待。当垃圾收集器探测到引用可达性后,它将改变reference对象到pending状态或者inactive状态。如果refence对象在创建的时候包含一个queue时,它将改变对象的状态到pending。如果没有queue,它直接将对象改变到inactive状态。新创建的reference对象是active状态。
- Pending: 一个refer对象在pending-reference集合中,等待reference线程处理器加入到队列中。如果对象在创建的时候没有注入队列,则引用对象不会有此状态。
- Enqueued:当一个refer对象加入到队列后,就变为enqueued状态。
- Inactive:不能发生任何状态的转化
总结:当垃圾收集回收引用对象时,如果对象在创建的时候注入了队列,则把回收的对象加入到队列。加入队列的目的是为了程序员手动删除被垃圾回收的对象。为什么还要手动删除被回收的引用对象了?我们看一下WeakHashMap的源码实现。
/**
* The entries in this hash table extend WeakReference, using its main ref
* field as the key.
*/
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
从上面的源码中可以看出,在创建Entry的时候注入了ReferenceQueue队列。由于Entry继承了WeakReference,但是并没有包装成WeakReference。
那么为什么不把Entry直接包装成WeakReference? 原因很简单,加入把Entry包装成WeakReference,就不能使用Hash的功能,依据Key定位value。
通过在Entry的构造函数中调用super(key)。把key包装成WeakReference对象,当垃圾收集器判定到只有引用可达性时,就把key设置为null。程序员通过迭代queue中的元素,把table中的key为null的元素删除。
2. Reference内部的实例字段及类字段
/* 被GC处理 */
private T referent;
//引用队列
volatile ReferenceQueue<? super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
@SuppressWarnings("rawtypes")
Reference next;
/* active状态:是discovered 引用集合中下一个元素通过GC维护
* pending: 是pending list 中的下一个元素
* otherwise: NULL
*/
//被vm使用
transient private Reference<T> discovered; /* used by VM */
/*等待进入队列的集合列表。垃圾收集器添加引用到这个集合,等待Reference-Handler线程移除它们。
*此字段是个类常量。所有的实例对象都有一个pending.
*/
private static Reference<Object> pending = null;
3. 上面源码中queue与next字段的关系
- Active:queue = ReferenceQueue当queue被注册时。或者queue=ReferenceQueue.NULL。next = null;表示此引用处于Active状态
- Pending:queue=ReferenceQueue当queue被注册时。next=this。next=this表示此引用处于Pending状态。
- Enqueued: queue = ReferenceQueue.ENQUEUED;next=队列的下一个实例。或者如过是队列的尾部,则是队列本身。
- Inactive: queue = ReferenceQueue.NULL; next = this.
添加pending引用元素到queue源码分析
当一个引用的队列不为空时,在进行垃圾回收的时候把状态转化为pending状态。
经过前面的介绍,当状态转变为pending状态时,discovered中保存的是pending list中的下一个元素。
把r从pending list中移除。加入到queue
pending = r.discovered;
r.discovered = null;
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) {
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 = 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;
}
}
} catch (OutOfMemoryError x) {
// Give other threads CPU time so they hopefully drop some live references
// and GC reclaims some space.
// Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
// persistently throws OOME for some time...
Thread.yield();
// retry
return true;
} catch (InterruptedException x) {
// retry
return true;
}
// Fast path for cleaners
if (c != null) {
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}