引用類型
在Java中,引用類型主要有四種,分別是:強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)和虛引用(Phantom Reference)。這些類型通常與垃圾回收機(jī)制有關(guān),用來描述對(duì)象的生命周期和可達(dá)性。下面詳細(xì)介紹每一種引用類型:
-
強(qiáng)引用(Strong Reference)
強(qiáng)引用是最常見的引用類型,當(dāng)在代碼中創(chuàng)建一個(gè)對(duì)象并賦值給一個(gè)引用變量時(shí),這個(gè)引用就是強(qiáng)引用。例如:String str = new String("Java");
只要強(qiáng)引用還存在,垃圾回收器永遠(yuǎn)不會(huì)回收被引用的對(duì)象。強(qiáng)引用可能導(dǎo)致內(nèi)存泄漏,因?yàn)榧词箤?duì)象已經(jīng)不再需要了,只要強(qiáng)引用還在,對(duì)象就不會(huì)被回收。
-
軟引用(Soft Reference)
軟引用是為了解決內(nèi)存敏感的緩存問題而設(shè)計(jì)的。通過java.lang.ref.SoftReference
類可以創(chuàng)建軟引用。垃圾回收器在系統(tǒng)內(nèi)存不足時(shí)會(huì)回收這些對(duì)象。軟引用通常用于實(shí)現(xiàn)內(nèi)存敏感的高速緩存,例如,圖片緩存。軟引用可以讓緩存的對(duì)象在內(nèi)存充足時(shí)被保留,而在內(nèi)存不足時(shí)被回收。SoftReference<String> softReference = new SoftReference<>(new String("Java"));
-
弱引用(Weak Reference)
弱引用通過java.lang.ref.WeakReference
類實(shí)現(xiàn)。弱引用不阻止它的對(duì)象被垃圾回收器回收。垃圾回收器一旦發(fā)現(xiàn)只有弱引用指向的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它。弱引用比軟引用更弱,它主要用于實(shí)現(xiàn)沒有阻止垃圾收集的引用鏈,例如,常見于元數(shù)據(jù)、查找大型結(jié)構(gòu)的關(guān)鍵等。WeakReference<String> weakReference = new WeakReference<>(new String("Java"));
-
虛引用(Phantom Reference)
虛引用是最弱的一種引用類型,通過java.lang.ref.PhantomReference
類實(shí)現(xiàn)。一個(gè)具有虛引用的對(duì)象,跟沒有引用一樣,在任何時(shí)候都可能被垃圾回收器回收。設(shè)置虛引用的唯一目的是在這個(gè)對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知。虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用。虛引用主要用于跟蹤對(duì)象被垃圾回收的活動(dòng),例如,確保對(duì)象完全銷毀后進(jìn)行某些特定資源的清理。PhantomReference<String> phantomReference = new PhantomReference<>(new String("Java"), new ReferenceQueue<>());
弱引用
合理的使用弱引用可以解決部分內(nèi)存泄漏的問題。
ThreadLocal
ThreadLocal是Java中多線程編程中一個(gè)重要的類。它能夠?qū)⒅荡鎯?chǔ)在當(dāng)前線程中,不與其他線程共享,同時(shí)讓編碼者能夠跨越多個(gè)層級(jí)獲取到該值。比如在Web服務(wù)中我們可以使用ThreadLocal存儲(chǔ)請(qǐng)求的id,從而使得該次請(qǐng)求中所有的日志輸出都攜帶上請(qǐng)求id,方便后續(xù)的異常排查;又或是如同Spring Transaction的實(shí)現(xiàn)一樣,將Connection存儲(chǔ)在ThreadLocal,讓一個(gè)線程綁定一個(gè)Connection,實(shí)現(xiàn)事務(wù)機(jī)制。
ThreadLocal實(shí)現(xiàn)解析:
ThreadLocal的本身的實(shí)現(xiàn)就是弱引用使用的一個(gè)經(jīng)典案例。
上面是ThreadLocal的get方法,可以看到它先是獲取當(dāng)前的線程之后再獲取了線程中的ThreadLocalMap實(shí)例,接著從該Map中獲取到具體的值。也就是說ThreadLocal實(shí)際上是通過每一個(gè)線程中存儲(chǔ)一個(gè)單獨(dú)的ThreadLocalMap實(shí)例來實(shí)現(xiàn)的。
接下來查看ThreadLocalMap的源碼:
ThreadLocalMap是ThreadLocal的一個(gè)內(nèi)部類,它的Entry繼承自WeakReference,結(jié)合Entry的構(gòu)造方法可以發(fā)現(xiàn)Entry是持有了一個(gè)指向ThreadLocal實(shí)例的弱引用并且使用該弱引用作為Key。如此實(shí)現(xiàn),當(dāng)外部沒有指向該ThreadLocal的軟引用、強(qiáng)引用之后,該ThreadLocal實(shí)例將會(huì)被直接回收。這樣做帶來的一個(gè)好處就是,在我們不需要該ThreadLocal對(duì)象之后,這個(gè)ThreadLocal能夠及時(shí)的被GC回收,保證不會(huì)因此導(dǎo)致內(nèi)存泄漏。
有一個(gè)經(jīng)典問題:ThreadLocal是如何導(dǎo)致內(nèi)存泄漏的?
首先ThreadLocal自身提供了remove方法,上面說了ThreadLocalMap的實(shí)現(xiàn)保證了程序不會(huì)因?yàn)門hreadLocal導(dǎo)致內(nèi)存泄漏,那這個(gè)問題是不是與上面所說的沖突了?其實(shí)不然,從前面的代碼可以看到ThreadLocalMap的Entry.key雖然是弱引用,但是Entry.value并不是弱引用。當(dāng)ThreadLocal被GC回收之后,并且我們?cè)诨厥罩皼]有顯示調(diào)用remove方法,我們便無法訪問到對(duì)應(yīng)的Entry,從而將該Entry刪除,那么始終便存在一條這樣的引用鏈路Thread->ThreadLocalMap->Entry->value
,在線程被結(jié)束之前該value以及該Entry無法被回收,若是該線程無法沒及時(shí)結(jié)束,那么就有可能導(dǎo)致內(nèi)存泄漏。
可能會(huì)有人問為何不將Entry以及Entry.value也實(shí)現(xiàn)為弱引用?答案也很簡單,若是實(shí)現(xiàn)為弱引用,那么很多情況下通過ThreadLocal獲取一開始存入的值都將為null,在非特殊情況下,
ThreadLocal的作用可以說是等于零。
綜上所述,在編碼的時(shí)候,只要合理的調(diào)用remove方法其實(shí)就能夠避免因ThreadLocal導(dǎo)致的內(nèi)存泄漏。
WeakHashMap
Java中還有其他地方也使用到了弱引用,比如WeakHashMap。
- 相比ThreadLocal,WeakHashMap也是通過將對(duì)象的軟引用作為entry.key,但是與ThreadLocal最大的不同就是它同時(shí)使用了ReferenceQueue,ReferenceQueue提供了在引用對(duì)象被GC回收時(shí),通知給程序的功能,WeakHashMap借此實(shí)現(xiàn)了在get等方法調(diào)用時(shí),自動(dòng)刪除被回收key所對(duì)應(yīng)條目的功能。
使用案例:文章來源:http://www.zghlxwxcb.cn/news/detail-850184.html
import java.util.WeakHashMap;
public class WeakCache {
private final WeakHashMap<Key, BigObject> weakMap = new WeakHashMap<>();
public void put(Key key, BigObject object) {
weakMap.put(key, object);
}
public BigObject get(Key key) {
return weakMap.get(key);
}
// Key類用于映射的鍵
class Key {
// ...
}
// BigObject類是一個(gè)大對(duì)象,占用大量內(nèi)存
class BigObject {
// ...
}
}
在WeakCache
類中,通過WeakHashMap
實(shí)現(xiàn)了一個(gè)弱引用鍵的緩存。當(dāng)Key沒有在其他地方被強(qiáng)引用時(shí),這個(gè)Key-Value映射隨時(shí)都可能被自動(dòng)移除。文章來源地址http://www.zghlxwxcb.cn/news/detail-850184.html
到了這里,關(guān)于Java中的各種引用類型以及部分引用的相關(guān)例子的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!