目錄
- Reference 狀態(tài)
1.1 Active
1.2 Pending
1.3 Enqueued
1.4 Inactive - reference-hannel 線程
2.1 啟動線程
2.2 線程執(zhí)行
1.Reference 狀態(tài)
每個Reference狀態(tài)列表是Active瘪松、Pending瓶逃、Enqueued、Inactive (本文也將基于以下這四種狀態(tài)做出源碼解讀)
狀態(tài)間主要的標(biāo)識:
用Reference的成員變量queue與next(類似于單鏈表中的next)來標(biāo)識
ReferenceQueue<? super T> queue;
Reference next; //當(dāng)狀態(tài)不為active,gc自動賦值為this
有了這些約束说庭,GC 只需要檢測next字段就可以知道是否需要對該引用對象(reference)采取特殊處理
- 如果next為null昼捍,那么說明該引用為Active狀態(tài)
- 如果next不為null,那么 GC 應(yīng)該按其正常邏輯處理該引用。
什么特殊處理腰素?
GC在狀態(tài)active發(fā)送變化時,會主動把 weakReference中的 reference置null雪营,PhantomReference 中的 reference 無視 (PhantomReference的get方法重寫返回null弓千,可能就因為這原因就沒有置null了),并給next賦值this,以及pending等献起。(別問我為啥洋访,測出來的。谴餐。姻政。。培訓(xùn)出來就這水平了)
1.1 Active
描述:
active 狀態(tài)下的reference會受到GC的特別對待总寒,GC在發(fā)現(xiàn)對象的可達性變成合適的狀態(tài)后將變更reference狀態(tài)扶歪,具體是變更為pending還是Inactive,取決于是否注冊了 queue
特點:
//如果構(gòu)造參數(shù)中沒指定queue摄闸,那么queue為ReferenceQueue.NULL
//否則為構(gòu)造參數(shù)中傳遞過來的queue
queue = ReferenceQueue || ReferenceQueue.NULL
next = null
源碼分析:
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
1.2 Pending
描述:
pending-Reference列表中的引用都是這個狀態(tài)。
它們等著被內(nèi)部線程ReferenceHandler處理(調(diào)用ReferenceQueue.enqueue方法)
沒有注冊的實例不會進入這個狀態(tài)!
特點:
//ReferenceQueue 是注冊的隊列,從構(gòu)造參數(shù)哪里傳過來的
queue=ReferenceQueue
next = this
源碼分析:
- 想進Pending就必須要注冊隊列,ReferenceQueue.NULL 是不會enqueue
static boolean tryHandlePending(boolean waitForNotify) {
//some code
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
- next=this妹萨,這個應(yīng)該是GC賦的值年枕,只要reference的state一改變,這個next就莫名其妙被賦值了乎完。
3.似乎明白了 ReferenceQueue.ENQUEUED 的含義
public boolean isEnqueued() {
return (this.queue == ReferenceQueue.ENQUEUED);
}
1.3 Enqueued
描述:
調(diào)用ReferenceQueue.enqueued方法后的引用處于這個狀態(tài)中熏兄。
沒有注冊的實例不會進入這個狀態(tài)。
特點:
queue = ReferenceQueue.ENQUEUED;
next = queue的下一個引用, 如果已經(jīng)queue是最后一個元素树姨,那么就是自身.
源碼分析:
可以看出 整體是一種先進后出的棧結(jié)構(gòu)摩桶。head作為棧頭。r.next指向成員變量 head帽揪,如果head為null硝清,那么就指向自己(為了后面的壓棧好找元素)
private volatile Reference<? extends T> head = null;
boolean enqueue(Reference<? extends T> r) {
//some code
r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
//some code
}
1.4 Inactive
描述:
最終狀態(tài),處于這個狀態(tài)的引用對象转晰,狀態(tài)不會在改變芦拿。
兩種途徑到這個狀態(tài):
1.referenceQueue 調(diào)用 reallyPoll ,
2.不注冊 referenceQueue查邢,reference state變化時蔗崎。
特點:
queue = ReferenceQueue.NULL;
next = this
源碼分析:
private Reference<? extends T> reallyPoll() { /* Must hold lock */
Reference<? extends T> r = head;
//some code
r.queue = NULL;
r.next = r;
//some code
}
2.reference-hannel 線程
2.1啟動線程
- 說明jvm一啟動線程就跟著啟動了
- 提供了一個 JavaLangRefAccess 來提供外接接口訪問
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {//提供一個訪問接口
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
2.2 線程執(zhí)行
- pending == null 時,會阻塞線程扰藕,所以不會一直調(diào)用缓苛。gc肯定notify了
- Cleaner 是個重要的接口,非常適合做資源回收邓深。
- 如果注冊了引用隊列未桥,會將引用enqueue進去番官。
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;
}