Java的引用和引用隊(duì)列
Java的軟引用,弱引用和虛引用在都是繼承了Reference這個(gè)類,而且經(jīng)常配合ReferenceQueue來(lái)使用。接下來(lái)我們將了解一下這兩個(gè)類。
Reference
主要是負(fù)責(zé)內(nèi)存的一個(gè)狀態(tài),當(dāng)然它還和java虛擬機(jī),垃圾回收器打交道。Reference類首先把內(nèi)存分為4種狀態(tài)Active,Pending,Enqueued,Inactive。
- Active,一般來(lái)說(shuō)內(nèi)存一開(kāi)始被分配的狀態(tài)都是 Active
- Pending 大概是指快要被放進(jìn)隊(duì)列的對(duì)象,也就是馬上要回收的對(duì)象
- Enqueued 就是對(duì)象的內(nèi)存已經(jīng)被回收了,我們已經(jīng)把這個(gè)對(duì)象放入到一個(gè)隊(duì)列中,方便以后我們查詢某個(gè)對(duì)象是否被回收
- Inactive就是最終的狀態(tài),不能再變?yōu)槠渌鼱顟B(tài)
主要屬性
private T referent;
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered;
private static Reference<Object> pending = null;
- referent表示其引用的對(duì)象,即在構(gòu)造的時(shí)候需要被包裝在其中的對(duì)象。GC會(huì)根據(jù)不同Reference來(lái)特別對(duì)待
- queue 是對(duì)象即將被回收時(shí)所要通知的隊(duì)列。當(dāng)對(duì)象即將被回收時(shí),整個(gè)reference對(duì)象,而不僅僅是被回收的對(duì)象,會(huì)被放到queue 里面,然后外部程序即可通過(guò)監(jiān)控這個(gè) queue 即可拿到相應(yīng)的數(shù)據(jù)了。
- next 即當(dāng)前引用節(jié)點(diǎn)所存儲(chǔ)的下一個(gè)即將被處理的節(jié)點(diǎn)。這個(gè)用于實(shí)現(xiàn)一個(gè)單向循環(huán)鏈表,用以將保存需要由ReferenceHandler處理的引用
- discovered 表示要處理的對(duì)象的下一個(gè)對(duì)象。
- pending 是等待被入隊(duì)的引用列表。此屬性保存一個(gè)PENDING的隊(duì)列,配合上述next一起使用
靜態(tài)代碼塊
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);
}
});
}
當(dāng) Refrence 類被加載的時(shí)候,會(huì)執(zhí)行靜態(tài)代碼塊。在靜態(tài)代碼塊里面,會(huì)啟動(dòng) ReferenceHandler 線程,并設(shè)置線程的級(jí)別為最大級(jí)別, Thread.MAX_PRIORITY。
ReferenceHandler
private static class ReferenceHandler extends Thread {
private static void ensureClassInitialized(Class<?> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
// pre-load and initialize InterruptedException and Cleaner classes
// so that we don't get into trouble later in the run loop if there's
// memory shortage while loading/initializing them lazily.
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
該類是一個(gè)靜態(tài)內(nèi)部類,繼承了Thread類,run方法里是一個(gè)死循環(huán)。
tryHandlePending
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;
}
在 tryHandlePending 方法里面,檢查 pending 是否為 null,如果pending不為 null,則將 pending 進(jìn)行 enqueue,否則線程進(jìn)入 wait 狀態(tài)。
ReferenceQueue
引用隊(duì)列,在檢測(cè)到適當(dāng)?shù)目傻竭_(dá)性更改后,垃圾回收器將已注冊(cè)的引用對(duì)象添加到隊(duì)列中,ReferenceQueue實(shí)現(xiàn)了入隊(duì)(enqueue)和出隊(duì)(poll),還有remove操作,內(nèi)部元素head就是泛型的Reference。