引用類型
引用包括強(qiáng)引用(Strong ref)稼锅、軟引用(Soft ref)、弱引用(weak ref)僚纷、虛引用(幻象引用矩距,PhantomReference)。不同的引用類型怖竭, 主要體現(xiàn)的是對(duì)象不同的可達(dá)性(reachable) 狀態(tài)
和對(duì)垃圾收集的影響锥债。
- 強(qiáng)引用 最常見的普通對(duì)象引用,只要還有強(qiáng)引用指向一個(gè)對(duì)象痊臭,就能表明對(duì)象還“活著”哮肚,垃圾收集器不會(huì)碰這種對(duì)象。
- 軟引用 相對(duì)強(qiáng)引用弱化一些的引用广匙,可以讓對(duì)象豁免一些垃圾收集允趟,只有當(dāng)JVM認(rèn)為內(nèi)存不足時(shí),才會(huì)去試圖回收軟引用指向的對(duì)象鸦致。JVM會(huì)確保在拋出OutOfMemoryError之前潮剪,清理軟引用指向的對(duì)象涣楷。軟引用通常用來實(shí)現(xiàn)內(nèi)存敏感的緩存,如果還有空閑內(nèi)存抗碰,就可以暫時(shí)保留緩存狮斗,當(dāng)內(nèi)存不足時(shí)清理掉,這樣就保證了使用緩存的同時(shí)弧蝇,不會(huì)耗盡內(nèi)存碳褒。
- 弱引用 不能使對(duì)象豁免垃圾收集, 僅僅是提供一種訪問在弱引用狀態(tài)下對(duì)象的途徑看疗。維護(hù)一種非強(qiáng)制性的映射關(guān)系沙峻,如果試圖獲取時(shí)對(duì)象還在,就使用它鹃觉,否則重現(xiàn)實(shí)例化专酗。它同樣是很多緩存實(shí)現(xiàn)的選擇。
- 虛引用 不能通過它訪問對(duì)象盗扇。幻象引用僅僅是提供了一種確保對(duì)象被finalize以后沉填,做某些事情的機(jī)制疗隶,比如,通常用來做所謂的Post-Mortem清理機(jī)制翼闹,我在專欄上一講中介紹的Java平臺(tái)自身Cleaner機(jī)制等斑鼻。
除了幻象引用(因?yàn)間et永遠(yuǎn)返回null),如果對(duì)象還沒有被銷毀猎荠,都可以通過get方法獲取原有對(duì)象坚弱。這意味著,利用軟引用和弱引用关摇,我們可以將訪問到的對(duì)象荒叶,重新指向強(qiáng)引用,也就是人為的改變了對(duì)象的可達(dá)性狀態(tài)输虱!在對(duì)象執(zhí)行finallize方法時(shí)也可以使用強(qiáng)引用來挽留對(duì)象些楣。
引用隊(duì)列(ReferenceQueue)
當(dāng)gc(垃圾回收線程)準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還僅有軟引用(或弱引用宪睹,或虛引用)指向它愁茁,就會(huì)在回收該對(duì)象之前,把這個(gè)軟引用對(duì)象(或弱引用亭病,或虛引用鹅很,注意不是Reference對(duì)象的referent。)加入到與之關(guān)聯(lián)的引用隊(duì)列(ReferenceQueue)中罪帖。如果一個(gè)軟引用(或弱引用促煮,或虛引用)對(duì)象本身在引用隊(duì)列中邮屁,就說明該引用對(duì)象所指向的對(duì)象被回收了∥垡穑可以通過擴(kuò)展軟引用(或弱引用樱报,或虛引用)來實(shí)現(xiàn)在Referent對(duì)象入隊(duì)時(shí),同時(shí)做一些清理相關(guān)對(duì)象的操作泞当。
軟引用迹蛤、弱引用、虛引用對(duì)象本身是個(gè)強(qiáng)引用襟士,不會(huì)自動(dòng)被gc回收盗飒,需要手動(dòng)進(jìn)行調(diào)用poll或remove從隊(duì)列里取出,當(dāng)不再被引用時(shí)自動(dòng)回收陋桂。WeakHahsMap 的實(shí)現(xiàn)原理簡(jiǎn)單來說就是HashMap里面的條目 Entry繼承了 WeakReference逆趣,那么當(dāng) Entry 的 key 不再被使用(即,引用對(duì)象不可達(dá))且被 GC 后嗜历,那么該 Entry 就會(huì)進(jìn)入到 ReferenceQueue 中宣渗。當(dāng)我們調(diào)用WeakHashMap 的get和put方法會(huì)有一個(gè)副作用,即清除無效key對(duì)應(yīng)的Entry(WeakHashMap的Entry擴(kuò)展了WeakReference類梨州,同時(shí)保存了key的hashcode)痕囱。
TODO 使用引用隊(duì)列檢測(cè)內(nèi)存泄露
Reachability Fence
按照J(rèn)ava語言規(guī)范,如果一個(gè)對(duì)象沒有指向強(qiáng)引用暴匠,就符合垃圾收集的標(biāo)準(zhǔn)鞍恢,有些時(shí)候,對(duì)象本身并沒有強(qiáng)引用每窖, 但是也許它的部分屬性還在被使用帮掉,這樣就導(dǎo)致詭異的問題趣竣,所以我們需要一個(gè)方法赁咙,在沒有強(qiáng)引用情況下拢军,通知JVM對(duì)象是在被使用的蒜埋。
class Resource {
private static ExternalResource[] externalResourceArray = ...
int myIndex;
Resource(...) {
myIndex = ...
externalResourceArray[myIndex] = ...;
...
}
protected void finalize() {
externalResourceArray[myIndex] = null;
...
}
public void action() {
try {
// 需要被保護(hù)的代碼
int i = myIndex;
Resource.update(externalResourceArray[i]);
} finally {
// 調(diào)用reachbilityFence码秉, 明確保障對(duì)象strongly reachable
Reference.reachabilityFence(this);
}
}
}
方法action的執(zhí)行菌瘫,依賴于對(duì)象的部分屬性奈偏,所以被特定保護(hù)了起來飞几。否則后室,如果我們?cè)诖a中調(diào)用new Resource().action()
缩膝, 那么就可能會(huì)出現(xiàn)困擾,因?yàn)闆]有強(qiáng)引用指向我們創(chuàng)建出來的Resource對(duì)象岸霹,JVM對(duì)它進(jìn)行finalize操作是完全合法的疾层。類似的書寫結(jié)構(gòu),在異步編程中似乎是很普遍的贡避,因?yàn)楫惒骄幊讨型粫?huì)用傳統(tǒng)的“執(zhí)行->返回->使用”的結(jié)構(gòu)痛黎。