java的強(qiáng)引用、軟引用敦迄、弱引用恋追、幻象引用,引用隊(duì)列總結(jié)
- java除了原始數(shù)據(jù)類型的變量罚屋,其他所有都是引用類型苦囱。
- 引用分為強(qiáng)引用、軟引用脾猛、弱引用撕彤、幻象引用,這幾種引用影響著對象的回收
強(qiáng)引用
- 強(qiáng)引用:形如Object object = new Object();這樣就是典型的強(qiáng)引用猛拴,被強(qiáng)引用引用的對象不會(huì)被垃圾收集器主動(dòng)回收羹铅,JVM寧愿拋出OutOfMemoryError運(yùn)行時(shí)錯(cuò)誤(OOM),使程序異常終止愉昆,也不會(huì)靠隨意回收具有強(qiáng)引用的“存活”對象來解決內(nèi)存不足的問題职员。對于一個(gè)普通的對象,如果沒有其他的引用關(guān)系跛溉,只要超過了引用的作用域或者顯式地將相應(yīng)強(qiáng)引用賦值為 null焊切,這個(gè)被引用的對象就是可以被垃圾回收器回收的(具體回收時(shí)機(jī)還是要看垃圾收集策略)扮授。
引用(Reference類)
- 先在這里說一下,軟引用(SoftReference)蛛蒙、弱引用(WeakReference)糙箍、幻象引用(PhantomReference)都是java.lang.ref.Reference的子類,這個(gè)Reference類主要有4個(gè)方法
- void clean();清除此參考對象牵祟。(此方法僅由Java代碼調(diào)用; 當(dāng)垃圾收集器清除引用時(shí)深夯,它直接執(zhí)行,而不調(diào)用此方法诺苹。)
- boolean enqueue();將此引用對象添加到其注冊的隊(duì)列(如果有)咕晋。
- T get();返回此引用對象的指示。(通過這個(gè)方法可以返回Reference所引用的對象收奔,可以重新變成強(qiáng)引用) 例如:軟引用引用的一個(gè)對象
MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
aRef = null;
//現(xiàn)在只有一個(gè)軟引用指向MyObject的這個(gè)對象掌呜,
//如果這個(gè)對象還沒有被回收,可以把他再次變?yōu)閺?qiáng)引用
if(aSoftRef.get() != null)
MyObject bRef = aSoftRef.get();
//這個(gè)時(shí)候MyObject這個(gè)對象又變成強(qiáng)引用
復(fù)制代碼
boolean isEnqueued();通過程序或垃圾收集器來告知這個(gè)引用對象是否已經(jīng)入隊(duì);
其中enqueue 和 isEnqueued 這兩個(gè)方法涉及到引用隊(duì)列坪哄,我們后面會(huì)講到质蕉。這里就先不解釋,留個(gè)印象就行翩肌。
軟引用(SoftReference)
軟引用通過SoftReference類實(shí)現(xiàn)模暗。軟引用的生命周期比強(qiáng)引用短一些。只有當(dāng) JVM 認(rèn)為內(nèi)存不足時(shí)念祭,才會(huì)去試圖回收軟引用指向的對象:即JVM 會(huì)確保在拋出 OutOfMemoryError 之前兑宇,清理軟引用指向的對象。軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用粱坤,如果軟引用所引用的對象被垃圾回收器回收隶糕,Java虛擬機(jī)就會(huì)把這個(gè)軟引用(注意是引用本身這個(gè)對象(就是Reference自己,并不是引用所引用的對象)加入到與之關(guān)聯(lián)的引用隊(duì)列中站玄。后續(xù)枚驻,我們可以調(diào)用ReferenceQueue的poll()方法來檢查是否有它所關(guān)心的對象被回收(因?yàn)樵谶@個(gè)隊(duì)列里面的引用所指向的對象都被回收了)。如果隊(duì)列為空株旷,將返回一個(gè)null,否則該方法返回隊(duì)列中前面的一個(gè)Reference對象再登。
應(yīng)用場景:軟引用通常用來實(shí)現(xiàn)內(nèi)存敏感的緩存。如果還有空閑內(nèi)存灾常,就可以暫時(shí)保留緩存霎冯,當(dāng)內(nèi)存不足時(shí)清理掉铃拇,這樣就保證了使用緩存的同時(shí)钞瀑,不會(huì)耗盡內(nèi)存。
弱引用(WeakReference)
弱引用通過WeakReference類實(shí)現(xiàn)慷荔。 弱引用的生命周期比軟引用短雕什。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否贷岸,都會(huì)回收它的內(nèi)存壹士。由于垃圾回收器是一個(gè)優(yōu)先級很低的線程,因此不一定會(huì)很快回收弱引用的對象偿警。弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用躏救,如果弱引用所引用的對象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中(和引用隊(duì)列一起使用同上面的軟引用)螟蒸。
應(yīng)用場景:弱應(yīng)用同樣可用于內(nèi)存敏感的緩存盒使。
幻象引用(PhantomReference)
幻象引用也叫虛引用,通過PhantomReference類來實(shí)現(xiàn)七嫌。無法通過虛引用訪問對象的任何屬性或函數(shù)少办。幻象引用僅僅是提供了一種確保對象被 finalize 以后诵原,做某些事情的機(jī)制英妓。如果一個(gè)對象僅持有虛引用,那么它就和沒有任何引用一樣绍赛,在任何時(shí)候都可能被垃圾回收器回收蔓纠。虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對象時(shí)惹资,如果發(fā)現(xiàn)它還有虛引用贺纲,就會(huì)在回收對象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中褪测。程序可以通過判斷引用隊(duì)列中是否已經(jīng)加入了虛引用猴誊,來了解被引用的對象是否將要被垃圾回收。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列侮措,那么就可以在所引用的對象的內(nèi)存被回收之前采取一些程序行動(dòng)(和引用隊(duì)列一起使用同上面軟引用跟弱引用)懈叹。
應(yīng)用場景:可用來跟蹤對象被垃圾回收器回收的活動(dòng),當(dāng)一個(gè)虛引用關(guān)聯(lián)的對象被垃圾收集器回收之前會(huì)收到一條系統(tǒng)通知
引用隊(duì)列(ReferenceQueue):
- Reference對象已經(jīng)不再具有存在的價(jià)值分扎,需要一個(gè)適當(dāng)?shù)那宄龣C(jī)制澄成,避免大量SoftReference對象帶來的內(nèi)存泄漏。在java.lang.ref包里還提供了ReferenceQueue畏吓。
- 前面說到在使用軟引用墨状、虛引用、幻象引用的時(shí)候可以指定一個(gè)引用隊(duì)列菲饼,在引用所引用的對象被回收后引用本身就會(huì)進(jìn)入引用隊(duì)列肾砂。
- 使用例子如下
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(aMyObject,queue);
復(fù)制代碼
- 通過引用隊(duì)列可以看到哪些Reference對象所引用的對象已經(jīng)被回收,當(dāng)調(diào)用引用隊(duì)列的poll()方法就可以返回除隊(duì)列中的失去所引用對象的Reference對象
- 利用這個(gè)方法宏悦,我們可以檢查哪個(gè)SoftReference所軟引用的對象已經(jīng)被回收镐确。于是我們可以把這些失去所軟引用的對象的SoftReference對象清除掉包吝。
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}
復(fù)制代碼
一些應(yīng)用
- 軟引用和弱引用可以用來做一些內(nèi)存敏感的緩存,空間足夠的時(shí)候就緩存對象源葫,不夠的時(shí)候就回收诗越,不會(huì)拋出ome(內(nèi)存溢出異常,網(wǎng)上有很多這種小demo息堂,我這里就不贅述了)
- entry的key是弱引用從他的構(gòu)造函數(shù)可以看出嚷狞,為什么是弱引用呢,因?yàn)楫?dāng)threadlocal對象不在使用的時(shí)候?qū)⑵渲梦籲ull荣堰,但是這個(gè)時(shí)候entry的key還是指向的threadlocal對象感耙,如果這個(gè)時(shí)候是強(qiáng)引用就會(huì)導(dǎo)致threadlocal對象沒辦法回收會(huì)造成內(nèi)存泄漏,所以改成弱引用的話當(dāng)只有一個(gè)弱引用的entry的key指向threadlocal對象的時(shí)候Threadlocal對象在垃圾回收的時(shí)候就會(huì)被回收掉持隧。(這里涉及一些ThreadLocal的底層實(shí)現(xiàn)即硼,大家可以看我這一篇博客(juejin.im/post/684490…
在強(qiáng)引用、軟引用屡拨、弱引用只酥、幻象引用的介紹我引用了一些其他博客下的評論衣洁,由于不好貼鏈接就只能聲明一下吧冠骄。
Glide中的弱引用及引用隊(duì)列使用
glide中弱引用gc回收之后,再回到內(nèi)存緩存:具體操作就是弱引用存入到引用隊(duì)列中之后凿蒜,再從引用隊(duì)列放入到內(nèi)存緩存哥艇,里面主要做了一個(gè)弱引用回收的監(jiān)聽绝编,一旦監(jiān)聽到了弱引用被回收,立馬就去引用隊(duì)列中將其取出到內(nèi)存緩存貌踏,然后再由從引用隊(duì)列中移除十饥。
Glide中為什么有弱引用緩存
因?yàn)長ru算法,他是最近最少使用原則的回收策略祖乳;所以可能導(dǎo)致正在使用的圖片被回收逗堵,如果用了弱引用的緩存,就可以保證它正在使用的時(shí)候不會(huì)被回收眷昆,這樣就實(shí)現(xiàn)了正在使用資源與緩存資源的隔離蜒秤;正在使用資源不收到Lru算法的回收影響。
而且弱引用對象亚斋,他的get()作媚,然后賦值給強(qiáng)引用對象的時(shí)候,他就是多了個(gè)強(qiáng)引用了帅刊,就不會(huì)被回收了纸泡;當(dāng)沒有g(shù)et的弱引用被回收了,就會(huì)進(jìn)入到引用隊(duì)列厚掷,對弱引用進(jìn)行回收監(jiān)聽弟灼,當(dāng)觸發(fā)了回收,就立馬從引用隊(duì)列中獲取到相應(yīng)的對象加入到內(nèi)存緩存中冒黑。