Java中弱引用、軟引用畔派、虛引用及強引用的區(qū)別

前言

從Jdk1.2開始铅碍,在java.lang.ref包下就提供了三個類:SoftReference(軟引用),PhantomReference(虛引用)和WeakReference(弱引用)线椰,它們分別代表了系統(tǒng)對對象的中的三種引用方式:軟引用胞谈,虛引用以及弱引用。因此java語言對對象的引用有如下四種憨愉》成可能有些工作經(jīng)驗比較豐富的java程序員都不太明白這幾種引用的區(qū)別,僅僅只是知道而已配紫。但是知道弱引用和軟引用的概念以及如何使用它們是兩回事径密,引用類在垃圾回收工作上有著重要的作用。對于經(jīng)驗不夠豐富的小白以及那些和我經(jīng)驗差不多的程序員來說躺孝,他們在第一次聽說這些引用類的時候享扔,我想它們應(yīng)該和我是一樣的感覺:wtf?這是個什么東西括细?我怎么學(xué)了java那么久都沒聽說過伪很?

再說說我為什么會看這個的?我司的產(chǎn)品其實前身是一個測試奋单,我真是想破了頭锉试,我都想不出來為什么公司會讓一個測試做產(chǎn)品。好了览濒,言歸正傳呆盖,以下是我和我司的產(chǎn)品對話:

產(chǎn)品: 小李啊,我們這個app需要加一個讀取銀行卡的功能贷笛,你抽空做一下应又。具體要求是進入這個界面的時候,用戶就可以開始讀卡了乏苦,讀卡的方法XX已經(jīng)給你封裝好了株扛,你直接調(diào)用就可以了尤筐。

我: 好的,沒問題洞就。大概半個小時做完盆繁。內(nèi)心竊喜,握草旬蟋,這簡直是太簡單了油昂,還好我之前做過循環(huán)輪播條的功能,還記得當(dāng)初的思路倾贰,就是用Handler發(fā)送延時消息就可以了冕碟,這個功能嘛,照葫蘆畫瓢匆浙,直接在讀卡失敗的回調(diào)接口中發(fā)消息安寺,繼續(xù)調(diào)用讀卡方法就行了,一直讀首尼, 直到讀卡成功后跳出循環(huán)我衬。哎呀,我簡直是天才饰恕。bingo挠羔!搞定!

半個小時候埋嵌,我司產(chǎn)品皺著眉頭走過來破加。。

產(chǎn)品: 哎雹嗦,小李范舀,你做的這個讀卡功能是不是有問題啊,最典型的一個問題就是了罪,沒加這個app之前锭环,app本身還運行挺流暢甜刻,怎么加了這個讀卡的功能全释,app沒點進去讀卡頁面一次,app就感覺慢一點臀栈,你趕緊看看這個是什么原因娃圆。

我: 這怎么可能玫锋?我寫的代碼肯定是沒問題的(真是赤裸的打臉)。

半個小時后

產(chǎn)品: 怎么樣讼呢,小李撩鹿,問題找到了嗎?

我: 問題找到了悦屏,我馬上解決节沦。

究其原因就是因為設(shè)計是一進來讀卡頁面就調(diào)用讀卡方法讀卡键思,如果讀卡失敗了,那么就會一直利用Handler發(fā)消息甫贯,而按下返回鍵的時候稚机,由于Handler還持有讀卡頁面(Activity)的引用,導(dǎo)致讀卡頁面內(nèi)存無法回收而導(dǎo)致的內(nèi)存泄漏(OOM)問題获搏。于是各種上google查解決辦法,其中有一種解決辦法就是利用引用去解決失乾。而我對這方面又不是太了解常熙,所以就花了點時間來了解一下,總結(jié)一下碱茁。至于Handler導(dǎo)致的內(nèi)存泄漏的原因和解決辦法裸卫,我將在另外一篇博客中詳細講解。

引用的應(yīng)用場景

  1. 我們都知道垃圾回收器會回收符合回收條件的對象的內(nèi)存纽竣,但并不是所有的程序員都知道回收條件取決于指向該對象的引用類型墓贿。這正是Java中弱引用和軟引用的主要區(qū)別。
  2. 如果一個對象只有弱引用指向它蜓氨,垃圾回收器會立即回收該對象聋袋,這是一種急切回收方式。
  3. 相對的穴吹,如果有軟引用指向這些對象幽勒,則只有在JVM需要內(nèi)存時才回收這些對象。
  4. 弱引用和軟引用的特殊行為使得它們在某些情況下非常有用港令。
  5. 例如:軟引用可以很好的用來實現(xiàn)緩存啥容,當(dāng)JVM需要內(nèi)存時,垃圾回收器就會回收這些只有被軟引用指向的對象顷霹。
  6. 而弱引用非常適合存儲元數(shù)據(jù)咪惠,例如:存儲ClassLoader引用。如果沒有類被加載淋淀,那么也沒有指向ClassLoader的引用遥昧。一旦上一次的強引用被去除,只有弱引用的ClassLoader就會被回收朵纷。

強引用(StrongReference)

  1. 強引用是我們在編程過程中使用的最簡單的引用渠鸽,如代碼String s=”abc”中變量s就是字符串對象”abc”的一個強引用。
  2. 任何被強引用指向的對象都不能被垃圾回收器回收柴罐,這些對象都是在程序中需要的徽缚。
  3. StringBuffer stringBuffer = new StringBuffer(); 此時的object也是一個強引用。我們在開發(fā)過程中使用的最多的就是強引用革屠。這個就不做過多的說明了凿试。

弱引用(WeakReference)

  1. 弱引用通過WeakReference類實現(xiàn)排宰,弱引用和軟引用很像但是弱引用的級別更低。

  2. 對于只有弱引用的對象而言那婉,當(dāng)系統(tǒng)垃圾回收機制運行時板甘,不管系統(tǒng)內(nèi)存是否足夠,總會回收該對象所占用的內(nèi)存(立即回收的方式)详炬。當(dāng)然并不是說當(dāng)一個對象只有弱引用時盐类,它就會立即被回收—-正如那些使用引用的對象一樣,必須等到系統(tǒng)垃圾回收機制運行時才會被回收呛谜。

    String str = new String("helloworld"); 
    WeakReference weakReference = new WeakReference(str); 
    str = null;
    
    全部代碼如下:
    
    public class Main {
        public static void main(String[] args) {
            String str = new String("Struts2權(quán)威指南");
            //創(chuàng)建一個弱引用在跳,讓這個弱引用引用到“Struts2權(quán)威指南”字符串
            WeakReference weakReference = new WeakReference(str);
            //切斷str引用和“Struts2權(quán)威指南”字符串之間的引用
            str = null;
            //取出弱引用所引用的對象
            System.out.println(weakReference.get());
            //強制進行垃圾回收
            System.gc();
            System.runFinalization();
            //再次取出弱引用所引用的對象
            System.out.println(weakReference.get());
        }
    }
    
    執(zhí)行的結(jié)果為:“Struts2權(quán)威指南”和null
    
    
  3. 由上面的代碼我們可以看到,程序首先創(chuàng)建了一個“Struts2權(quán)威指南”對象隐岛,并且讓str引用變量引用它猫妙。 WeakReference weakReference = new WeakReference(str); 時,系統(tǒng)首先創(chuàng)建了一個弱引用聚凹,和str指向同一個對應(yīng)割坠,當(dāng)程序執(zhí)行str = null;的時候,程序剪斷了str和“Struts2權(quán)威指南”之間的聯(lián)系妒牙,此時“Struts2權(quán)威指南”這時只有一個弱引用weakReference指向它彼哼,這個時候我們的程序依然可以通過這個弱引用來訪問該字符串常量,程序的第一個System.out.println(weakReference.get())依然是可以輸出“Struts2權(quán)威指南”的湘今,接下來程序強制垃圾回收沪羔,如果系統(tǒng)垃圾回收器啟動,那么將只有弱引用所引用的對象會被清除掉象浑,當(dāng)程序執(zhí)行到第二個System.out.println(weakReference.get())的時候蔫饰,通常輸出的就只是null值了。

  4. 總結(jié): 當(dāng)我們將str賦值為空愉豺,該對象就可以被垃圾回收器回收篓吁。因為該對象此時不再含有其他強引用,即使指向該對象的弱引用WeakReference也沒有辦法阻止垃圾回收器對該對象的回收蚪拦。相反的杖剪,如果該對象還有軟引用,str不會被立即回收驰贷,除非JVM需要內(nèi)存盛嘿。

軟引用(SoftReference)

軟引用需要通過SoftReference類來實現(xiàn),當(dāng)一個對象只具有軟引用時括袒,它有可能被垃圾回收機制回收次兆。對于只有軟引用的對象而言,當(dāng)系統(tǒng)內(nèi)存空間足夠時锹锰,它不會被系統(tǒng)回收芥炭,程序也可使用該對象漓库;當(dāng)系統(tǒng)內(nèi)存空間不足時,系統(tǒng)將會回收它园蝠。軟引用通常用于對內(nèi)存敏感的程序中渺蒿。

    public class Main {
        public static void main(String[] args) {
            String str = new String("Struts2權(quán)威指南");
            //創(chuàng)建一個軟引用,讓這個弱引用引用到“Struts權(quán)威指南”字符串
            SoftReference softReference = new SoftReference(str);
            //切斷str引用和“Struts2權(quán)威指南”字符串之間的引用
            str = null; 
            //取出軟引用所引用的對象
            System.out.println(softReference.get());
            //強制進行垃圾回收
            System.gc();
            System.runFinalization();
            //再次取出軟引用所引用的對象
            System.out.println(softReference.get());
        }
    }

    輸出結(jié)果是:“Struts2 權(quán)威指南”和“Struts2權(quán)威指南”

  1. 總結(jié):軟引用的代碼執(zhí)行過程和弱引用大致相同彪薛。根據(jù)打印的內(nèi)容就說明了當(dāng)JVM內(nèi)存充足的時候茂装,JVM是不會考慮去回收軟引用指向的內(nèi)存空間的。我們可以注意到善延,在上面的代碼中少态,有一行代碼是為str = null,雖然這行代碼是不能阻止垃圾回收器回收對象挚冤,但是可以延遲回收,這點和弱引用是不相同的赞庶。這也是軟引用適合做緩存而弱引用適合存儲元數(shù)據(jù)的根本原因所在训挡。
  2. 一個使用弱引用的典型例子是WeakHashMap,它是除HashMap和TreeMap之外歧强,Map接口的另一種實現(xiàn)澜薄。WeakHashMap有一個特點:map中的鍵值(keys)都被封裝成弱引用,也就是說一旦強引用被刪除摊册,WeakHashMap內(nèi)部的弱引用就無法阻止該對象被垃圾回收器回收肤京。

虛引用 (PhantomReference)

  1. 虛引用通過PhantomReference類實現(xiàn),虛引用完全類似于沒有引用茅特。虛引用對對象本身沒有太大的影響忘分,對象甚至感覺不到虛引用的存在。如果一個對象只有一個虛引用時白修。那它和沒有引用的效果大致相同妒峦。虛引用主要用于跟蹤對象被垃圾回收的狀態(tài),虛引用不能單獨使用兵睛,虛引用必須和引用隊列(ReferenceQueue)聯(lián)合使用肯骇。

    public class Main2 {
        public static void main(String[] args) {
               //創(chuàng)建一個字符串引用
            String str = new String("Struts2權(quán)威指南");
            //創(chuàng)建一個引用隊列
            ReferenceQueue referenceQueue = new ReferenceQueue();
            //創(chuàng)建一個虛引用 讓次虛引用引用到“Struts2權(quán)威指南”字符串
            PhantomReference phantomReference = new PhantomReference(str, referenceQueue);
            //切斷str和“Struts2權(quán)威指南”之間的引用關(guān)系
            str = null;
            //取出虛引用所引用的對象,并不能通過虛引用訪問被引用的對象祖很,
            //所以此處輸出的應(yīng)該是null
            System.out.println(phantomReference.get());
            //強制進行垃圾回收
            System.gc();
            System.runFinalization();
            //取出引用隊列最先進入隊列中的引用于phantomReference進行比較
            System.out.println(referenceQueue.poll() == phantomReference);
        }
    }
    
    輸出結(jié)果: null  和 true
    
    
  2. 因為系統(tǒng)無法通過虛引用來獲取被引用的對象笛丙,所以在執(zhí)行第一個System.out.println(phantomReference.get())的時候,打印出來的是空值(即使此時系統(tǒng)并未進行強制垃圾回收)假颇。當(dāng)程序強制垃圾回收后胚鸯,只有虛引用引用的字符串對象將會被垃圾回收,當(dāng)被引用的對象被回收后笨鸡,對象的引用將被添加到關(guān)聯(lián)的引用隊列中去(也就時說蠢琳,虛引用指向的字符串對象會被垃圾回收器回收啊终,而自己本身將被添加到與之關(guān)聯(lián)的引用隊列中去),因此我們才在第二個System.out.println(referenceQueue.poll() == phantomReference)中看到輸出的true

引用隊列(ReferenceQueue)

  1. 引用隊列由java.lang.ref.ReferenceQueue類來表示傲须,它用于保存被回收后對象的引用蓝牲。當(dāng)把軟引用、弱引用和引用隊列聯(lián)合使用的時候泰讽,系統(tǒng)在回收被引用的對象之后例衍,將把被回收對象對應(yīng)的應(yīng)用添加到關(guān)聯(lián)的引用隊列中。與軟引用和弱引用不同的是已卸,虛引用在對象被釋放之后佛玄,將把已經(jīng)回收對象對應(yīng)的虛引用添加它的關(guān)聯(lián)引用隊列中,這是得可以在對象被回收之前采取行動累澡。
  2. 軟引用和弱引用可以單獨使用梦抢,但是虛引用不能單獨使用,單獨使用虛引用沒有太大的意義愧哟。虛引用的主要作用就是跟蹤對象被垃圾回收的狀態(tài)奥吩,程序可以通過檢查與虛引用關(guān)聯(lián)的引用隊列中是否已經(jīng)包含了該虛引用,從而了解虛引用所引用對象是否即將被回收蕊梧。

總結(jié)

  1. 在應(yīng)用程序中霞赫,我們使用引用類可以邊面在程序執(zhí)行期間將對象留在內(nèi)存中。如果我們一軟引用肥矢,弱引用或虛引用的方式引用對象端衰,這樣垃圾收集器就能夠隨意的釋放對象。如果希望盡可能的減少程序在其生命周期中所占的內(nèi)存大小時甘改,這些引用類就很有好處旅东。

  2. 當(dāng)然必須指出的一個問題: 要使用這些特殊的引用類,就不能保留對對象的強引用十艾。如果保留了對對象的強引用玉锌,那么就會浪費這些類所提供的任何好處。

  3. 四種引用最主要的區(qū)別就是垃圾回收器回收的時機不同:

    1. 強引用: 我們經(jīng)常使用的一種引用疟羹≈魇兀基本上垃圾回收器不會主動的去回收
    2. 弱引用: 垃圾回收器會立刻回收弱引用。
    3. 軟引用: 在JVM沒有出現(xiàn)內(nèi)存不足的情況下榄融,垃圾回收器不會去主動回收軟引用
    4. 虛引用: 虛引用引用的字符串會被垃圾回收器回收参淫, 自己本身會被添加到關(guān)聯(lián)的引用隊列中去。
  4. 以上是我自己查找資料對這方面的全部理解愧杯。如果有錯誤涎才,還望各位指正。希望對不了解這塊的小伙伴們有所幫助。

轉(zhuǎn)自:https://blog.csdn.net/lqw_student/article/details/52947125

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耍铜,一起剝皮案震驚了整個濱河市邑闺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棕兼,老刑警劉巖陡舅,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伴挚,居然都是意外死亡靶衍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門茎芋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅眶,“玉大人,你說我怎么就攤上這事田弥√涡铮” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵偷厦,是天一觀的道長商叹。 經(jīng)常有香客問我,道長沪哺,這世上最難降的妖魔是什么沈自? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任酌儒,我火速辦了婚禮辜妓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忌怎。我一直安慰自己籍滴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布榴啸。 她就那樣靜靜地躺著孽惰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸥印。 梳的紋絲不亂的頭發(fā)上勋功,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音库说,去河邊找鬼狂鞋。 笑死,一個胖子當(dāng)著我的面吹牛潜的,可吹牛的內(nèi)容都是我干的骚揍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼啰挪,長吁一口氣:“原來是場噩夢啊……” “哼信不!你這毒婦竟也來了嘲叔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤抽活,失蹤者是張志新(化名)和其女友劉穎硫戈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酌壕,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡掏愁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卵牍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片果港。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖糊昙,靈堂內(nèi)的尸體忽然破棺而出辛掠,到底是詐尸還是另有隱情,我是刑警寧澤释牺,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布萝衩,位于F島的核電站,受9級特大地震影響没咙,放射性物質(zhì)發(fā)生泄漏猩谊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一祭刚、第九天 我趴在偏房一處隱蔽的房頂上張望牌捷。 院中可真熱鬧,春花似錦涡驮、人聲如沸暗甥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撤防。三九已至,卻和暖如春棒口,著一層夾襖步出監(jiān)牢的瞬間寄月,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工无牵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漾肮,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓合敦,卻偏偏與公主長得像初橘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容