轉(zhuǎn)載請把頭部出處鏈接和尾部二維碼一起轉(zhuǎn)載,本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/52637333
背景:收到公眾投稿念恍,《從面試題中看Java的Reference(引用)》六剥,分析的很不錯(cuò),總感覺少了實(shí)際的例子和應(yīng)用場景峰伙。于是結(jié)合自己工作中場景疗疟,小總結(jié)一下⊥ィ看下Agenda如下:
- 強(qiáng)引用
- 軟引用
- 弱引用
- 什么時(shí)候使用軟引用策彤,什么時(shí)候使用弱引用?
- 虛引用
一顿膨、強(qiáng)引用
Java中的引用锅锨,類似于C++的指針。通過引用恋沃,可以對堆中的對象進(jìn)行操作必搞。在某個(gè)函數(shù)中,當(dāng)創(chuàng)建了一個(gè)對象囊咏,該對象被分配在堆中恕洲,通過這個(gè)對象的引用才能對這個(gè)對象進(jìn)行操作。
假設(shè)以上代碼是在方法內(nèi)運(yùn)行的梅割,那么局部變量str將被分配在椝冢空間上,而對象StringBuffer實(shí)例户辞,被分配在堆空間中泌类。局部變量str指向StringBuffer實(shí)例所在的堆空間,通過str可以操作該實(shí)例底燎,那么str就是StringBuffer的引用刃榨。
此時(shí),運(yùn)行一個(gè)賦值語句:
那么双仍,str所指向的對象也將被str1所指向枢希,同時(shí)在局部棧空間上會(huì)分配空間存放str1變量朱沃。此時(shí)苞轿,該StringBuffer實(shí)例就有兩個(gè)引用茅诱。對引用的”==”操作用于表示兩個(gè)操作數(shù)所指向的堆空間地址是否相同,不表示兩個(gè)操作數(shù)所指向的對象是否相等搬卒。
強(qiáng)引用特點(diǎn):
- 強(qiáng)引用可以直接訪問目標(biāo)對象瑟俭。
- 強(qiáng)引用所指向的對象在任何時(shí)候都不會(huì)被系統(tǒng)回收。JVM寧愿拋出OOM異常秀睛,也不會(huì)回收強(qiáng)引用所指向的對象尔当。
- 強(qiáng)引用可能導(dǎo)致內(nèi)存泄露。
二蹂安、軟引用
軟引用是除了強(qiáng)引用外椭迎,最強(qiáng)的引用類型√镉可以通過java.lang.ref.SoftReference使用軟引用畜号。一個(gè)持有軟引用的對象,不會(huì)被JVM很快回收允瞧,JVM會(huì)根據(jù)當(dāng)前堆的使用情況來判斷何時(shí)回收简软。當(dāng)堆的使用率臨近閾值時(shí),才會(huì)回收軟引用的對象述暂。
看下我工作中使用到軟引用的場景痹升,加載一個(gè)1080x1920分辨率的圖,約900多K, 對于我們來說畦韭,這個(gè)圖已是非常大了疼蛾。
首先通過BitmapFactory.decodeStream構(gòu)造一個(gè)大圖bitmap
然后把這個(gè)bitmap轉(zhuǎn)成Drawble類型,構(gòu)成強(qiáng)引用艺配。
接著使用SoftReference構(gòu)造這個(gè)drawable對象的軟引用drawables.
最后通過軟引用的get()方法察郁,取得drawable對象實(shí)例的強(qiáng)引用,發(fā)現(xiàn)對象被未回收转唉。在GC在內(nèi)存充足的情況下皮钠,不會(huì)回收軟引用對象。
在實(shí)際中赠法,一起請求很多相關(guān)圖片麦轰,從網(wǎng)絡(luò),這時(shí)就會(huì)請求非常多的內(nèi)存空間砖织,導(dǎo)致內(nèi)存吃緊原朝,系統(tǒng)開始會(huì)GC。這次GC后镶苞,drawables.get()不再返回Drawable對象,而是返回null鞠评,這時(shí)屏幕上背景圖不顯示茂蚓,說明在系統(tǒng)內(nèi)存緊張的情況下,軟引用被回收。
使用軟引用以后聋涨,在OutOfMemory異常發(fā)生之前晾浴,這些緩存的圖片資源的內(nèi)存空間可以被釋放掉的,從而避免內(nèi)存達(dá)到上限牍白,避免Crash發(fā)生脊凰。
需要注意的是,在垃圾回收器對這個(gè)Java對象回收前茂腥,SoftReference類所提供的get方法會(huì)返回Java對象的強(qiáng)引用狸涌,一旦垃圾線程回收該Java對象之后,get方法將返回null最岗。所以在獲取軟引用對象的代碼中帕胆,一定要判斷是否為null,以免出現(xiàn)NullPointerException異常導(dǎo)致應(yīng)用崩潰般渡。
到底什么時(shí)候使用軟引用懒豹,什么時(shí)候使用弱引用呢笼沥?
個(gè)人認(rèn)為屈雄,如果只是想避免OutOfMemory異常的發(fā)生捐康,則可以使用軟引用比原。如果對于應(yīng)用的性能更在意钥弯,想盡快回收一些占用內(nèi)存比較大的對象祭刚,則可以使用弱引用译隘。
還有就是可以根據(jù)對象是否經(jīng)常使用來判斷坏匪。如果該對象可能會(huì)經(jīng)常使用的淘这,就盡量用軟引用剥扣。如果該對象不被使用的可能性更大些,就可以用弱引用铝穷。
另外钠怯,和弱引用功能類似的是WeakHashMap。WeakHashMap對于一個(gè)給定的key曙聂,其映射的存在并不阻止垃圾回收器對該鍵的回收晦炊,回收以后,其條目從映射中有效地移除宁脊。WeakHashMap使用ReferenceQueue實(shí)現(xiàn)的這種機(jī)制断国。
三、 弱引用
弱引用是一種比軟引用較弱的引用類型榆苞。在系統(tǒng)GC時(shí)稳衬,只要發(fā)現(xiàn)弱引用,不管系統(tǒng)堆空間是否足夠坐漏,都會(huì)將對象進(jìn)行回收薄疚。但是碧信,由于垃圾回收器的線程通常優(yōu)先級很低,因此街夭,并一不定能很快的發(fā)現(xiàn)持有弱引用的對象砰碴。這種情況下,弱引用對象可以存在較長的一段時(shí)間板丽。一旦一個(gè)弱引用對象被垃圾回收器回收呈枉,便會(huì)加入到一個(gè)注冊引用隊(duì)列中。
看一個(gè)工作中實(shí)例:播放器的播放Panel埃碱,是一個(gè)View猖辫,就是在視頻播放時(shí),可以show乃正、hide, 也可以拖拽進(jìn)度條之類住册,還有上面的音量,亮度調(diào)節(jié)等瓮具。這樣一個(gè)view荧飞,我們用弱引用,因?yàn)樵谝曨l播放過程中名党,不論硬解還是軟解叹阔,都將占用大量內(nèi)存。保證視頻的渲染效果传睹。
在VideoControllerView.java 有如下一段代碼:
在GC之前耳幢,弱引用對象并未被垃圾回收器發(fā)現(xiàn),因此通過mView.get()方法可以取得對應(yīng)的強(qiáng)引用欧啤。但是只要進(jìn)行垃圾回收睛藻,弱引用對象一旦被發(fā)現(xiàn),便會(huì)立即被回收邢隧,并加入注冊引用隊(duì)列中店印。此時(shí),再次通過mView.get()方法取得強(qiáng)引用就會(huì)失敗倒慧。
注意:軟引用按摘,弱引用都非常適合來保存那些可有可無的緩存數(shù)據(jù)。如果這樣做纫谅,當(dāng)系統(tǒng)內(nèi)存不足時(shí)炫贤,這些緩存數(shù)據(jù)會(huì)被回收,不會(huì)導(dǎo)致內(nèi)存溢出付秕。而當(dāng)內(nèi)存資源充足時(shí)兰珍,這些緩存數(shù)據(jù)又可以存在相當(dāng)長的時(shí)間。
四询吴、 虛引用
- 虛引用是所有引用類型中最弱的一個(gè)俩垃。一個(gè)持有虛引用的對象励幼,和沒有引用幾乎是一樣的,隨時(shí)都可能被垃圾回收器回收口柳。當(dāng)試圖通過虛引用的get()方法取得強(qiáng)引用時(shí),總是會(huì)失敗有滑。并且跃闹,虛引用必須和引用隊(duì)列一起使用,它的作用在于跟蹤垃圾回收過程毛好。
- 當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對象時(shí)望艺,如果發(fā)現(xiàn)它還有虛引用,就會(huì)在垃圾回收后肌访,銷毀這個(gè)對象找默,獎(jiǎng)這個(gè)虛引用加入引用隊(duì)列。
- 實(shí)際中幾乎沒用吼驶,暫不介紹惩激。
最后一張圖總結(jié)下: