引用
Java 虛擬機(jī)接管了所有的內(nèi)存分配與回收工作拴疤,極大地減少了程序員的工作量和錯(cuò)誤率。GC 在回收內(nèi)存時(shí)独泞,通常采用被稱為可達(dá)性分析的算法判斷一個(gè)對(duì)象是否可以回收呐矾。而在可達(dá)性分析中,對(duì)象的引用有著決定性的作用懦砂。在下圖中蜒犯,GC 從 GC Roots 開(kāi)始順著引用鏈往下尋找對(duì)象,發(fā)現(xiàn)當(dāng)前有引用的對(duì)象為object 1荞膘、object 2罚随、object 3、object 4羽资,而object 5淘菩、object 6、object 7雖然互相之間有引用但已經(jīng)無(wú)法從外部引用到削罩。因此瞄勾,圖中 object 1-4 為存活的對(duì)象,而 object 5-7 為可回收的對(duì)象弥激。
雖然 GC 能夠完成垃圾收集工作进陡,但是仍然無(wú)法避免 out of memory 。一方面在開(kāi)發(fā)過(guò)程中需要注意不再使用的引用設(shè)為 null 來(lái)釋放引用的對(duì)象微服,另一方面也需要從對(duì)象引用的角度考慮使用合適的引用類型更好地管理對(duì)象內(nèi)存趾疚。
引用的類型
Java 有4種類型的引用:strong(強(qiáng)引用),soft(軟引用)以蕴,weak(弱引用)和 phantom(虛引用)糙麦。
強(qiáng)引用:強(qiáng)引用是在 Java 中的普通引用。任何時(shí)候我們創(chuàng)建一個(gè)新的對(duì)象丛肮,默認(rèn)情況下創(chuàng)建一個(gè)強(qiáng)引用赡磅。例如類的靜態(tài)變量,從類被初始化之后便已經(jīng)分配內(nèi)存宝与,作為強(qiáng)引用對(duì)象不能被 GC 回收焚廊,需要等待虛擬機(jī)退出或類被卸載才能釋放引用被 GC 回收冶匹。
弱引用:弱引用無(wú)法保證對(duì)象一定存活于內(nèi)存中,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前咆瘟。我們可以使用 WeakReference 類來(lái)實(shí)現(xiàn)弱引用嚼隘。
軟引用:軟引用比弱引用稍強(qiáng)一點(diǎn),垃圾收集發(fā)生時(shí)弱引用一定會(huì)被回收袒餐,而軟引用會(huì)請(qǐng)求 GC 保留自己除非沒(méi)有其他選擇飞蛹,可以理解為只在將要發(fā)生內(nèi)存溢出時(shí) GC 才會(huì)回收軟引用。我們可以使用 SoftReference 類來(lái)實(shí)現(xiàn)軟引用灸眼。
虛引用:一個(gè)對(duì)象是否有虛引用的存在卧檐,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例幢炸。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被 GC 回收時(shí)收到一個(gè)系統(tǒng)通知泄隔。我們可以使用 PhantomReference 類來(lái)實(shí)現(xiàn)虛引用。當(dāng) GC 準(zhǔn)備回收一個(gè)對(duì)象時(shí)宛徊,如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前逻澳,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中闸天。開(kāi)發(fā)者可以通過(guò)判斷引用隊(duì)列中是否包含對(duì)象來(lái)判斷對(duì)象是否即將被回收,可以在回收之前做些處理斜做。
引用隊(duì)列:如果引用關(guān)聯(lián)了引用隊(duì)列苞氮,則 GC 回收對(duì)象內(nèi)存的時(shí)候會(huì)把引用加入到引用隊(duì)列中。當(dāng)引用隊(duì)列中包含引用時(shí)瓤逼,意味著引用指向的堆內(nèi)存中的對(duì)象被回收笼吟。
引用的應(yīng)用
構(gòu)建緩存
使用軟引用可以用于創(chuàng)建 Java 本地高速緩存,只要內(nèi)存仍然夠用緩存就不會(huì)被刪除霸旗,而一旦內(nèi)存緊張即將溢出時(shí)贷帮,GC 會(huì)刪除部分緩存釋放內(nèi)存。例如诱告,創(chuàng)建比較耗時(shí)影響性能的數(shù)據(jù)對(duì)象撵枢、一段時(shí)間內(nèi)可以重復(fù)使用的資源、不常變化的數(shù)據(jù)等精居,都可以使用軟引用構(gòu)建緩存锄禽,既能保證讀取性能,又不會(huì)導(dǎo)致內(nèi)存溢出靴姿。
WeakHashMap
WeakHashMap 是以弱引用鍵實(shí)現(xiàn)的哈希表沃但。當(dāng) WeakHashMap 中的鍵不再被強(qiáng)引用使用時(shí),GC 下次回收垃圾時(shí)將回收此鍵佛吓。WeakHashMap 中的鍵被回收后宵晚,哈希表的條目也會(huì)被 GC 回收垂攘。因此,WeakHashMap 可以用于臨時(shí)存儲(chǔ)一些不需要長(zhǎng)時(shí)間使用的對(duì)象坝疼,可以有效避免內(nèi)存溢出搜贤。
總結(jié)
Java 開(kāi)發(fā)中最常用的引用是強(qiáng)引用,通過(guò)new創(chuàng)建對(duì)象得到強(qiáng)引用钝凶。強(qiáng)引用會(huì)阻止 GC 釋放對(duì)象內(nèi)存仪芒,長(zhǎng)時(shí)間運(yùn)行容易導(dǎo)致內(nèi)存溢出。在開(kāi)發(fā)中耕陷,對(duì)于強(qiáng)引用變量在使用完畢后應(yīng)把值設(shè)置為 null 來(lái)幫助 GC 進(jìn)行垃圾回收掂名。在 Java 中還有另外三種引用類型,弱引用哟沫、軟引用和虛引用饺蔑。弱引用和軟引用可以用于構(gòu)建緩存和避免內(nèi)存泄露,虛引用可以用于獲知對(duì)象將被回收的通知并進(jìn)行處理嗜诀。
四種引用類型的對(duì)比如下: