Reference
我們先看下Reference.java中的幾個字段
public abstract class Reference<T> {
//引用的對象
private T referent;
//回收隊列
vol00000atile ReferenceQueue<? super T> queue;
//當該引用被加入到queue中的時候,該字段被設置爲queue中的下一個元素,以形成鏈表結構
volatile Reference next;
//在GC時,JVM底層會維護一個叫DiscoveredList的鏈表,存放的是Reference對象,discovered字段指向的就是鏈表中的下一個元素,由JVM設置
transient private Reference<T> discovered;
//進行線程同步的鎖對象
static private class Lock { }
private static Lock lock = new Lock();
//等待加入queue的Reference對象,在GC時由JVM設置,會有一個java層的線程(ReferenceHandler)源源不斷的從pending中提取元素加入到queue
private static Reference<Object> pending = null;
}
一個Reference對象的生命週期如下:
主要分爲Native層和Java層兩個部分。
Native層在GC時將需要被回收的Reference對象加入到DiscoveredList中(代碼在referenceProcessor.cpp中process_discovered_references方法),然後將DiscoveredList的元素移動到PendingList中(代碼在referenceProcessor.cpp中enqueue_discovered_ref_helper方法),PendingList的隊首就是Reference類中的pending對象。
Java層的代碼:
private static class ReferenceHandler extends Thread {
...
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) {
r = pending;
//如果是Cleaner對象,則記錄下來,下面做特殊處理
c = r instanceof Cleaner ? (Cleaner) r : null;
//指向PendingList的下一個對象
pending = r.discovered;
r.discovered = null;
} else {
//如果pending爲null就先等待,當有對象加入到PendingList中時,jvm會執行notify
if (waitForNotify) {
lock.wait();
}
// retry if waited
return waitForNotify;
}
}
}
...
// 如果時CLeaner對象,則調用clean方法進行資源回收
if (c != null) {
c.clean();
return true;
}
//將Reference加入到ReferenceQueue,開發者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
流程比較簡單:就是源源不斷的從PendingList中提取出元素,然後將其加入到ReferenceQueue中去,開發者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。
另外需要注意的是,對於Cleaner類型(繼承自虛引用)的對象會有額外的處理:在其指向的對象被回收時,會調用clean方法,該方法主要是用來做對應的資源回收,在堆外內存DirectByteBuffer中就是用Cleaner進行堆外內存的回收,這也是虛引用在java中的典型應用。