Java四種引用類型

引用與對(duì)象

每種編程語(yǔ)言都有自己操作內(nèi)存中元素的方式肝劲,例如在 C 和 C++ 里是通過(guò)指針诡壁,而在 Java 中則是通過(guò)“引用”麸澜。
在 Java 中一切都被視為了對(duì)象虹钮,但是我們操作的標(biāo)識(shí)符實(shí)際上是對(duì)象的一個(gè)引用(reference)坛梁。

//創(chuàng)建一個(gè)引用而姐,引用可以獨(dú)立存在,并不一定需要與一個(gè)對(duì)象關(guān)聯(lián)
String s;
復(fù)制代碼

通過(guò)將這個(gè)叫“引用”的標(biāo)識(shí)符指向某個(gè)對(duì)象划咐,之后便可以通過(guò)這個(gè)引用來(lái)實(shí)現(xiàn)操作對(duì)象了拴念。

String str = new String("abc");
System.out.println(str.toString());
復(fù)制代碼

在 JDK1.2 之前,Java中的定義很傳統(tǒng):如果 reference 類型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址褐缠,就稱為這塊內(nèi)存代表著一個(gè)引用政鼠。
Java 中的垃圾回收機(jī)制在判斷是否回收某個(gè)對(duì)象的時(shí)候,都需要依據(jù)“引用”這個(gè)概念队魏。
在不同垃圾回收算法中公般,對(duì)引用的判斷方式有所不同:

  • 引用計(jì)數(shù)法:為每個(gè)對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)引用指向它時(shí)胡桨,計(jì)數(shù)器就加1官帘,當(dāng)引用失效時(shí),計(jì)數(shù)器就減1昧谊,當(dāng)計(jì)數(shù)器為0時(shí)刽虹,則認(rèn)為該對(duì)象可以被回收(目前在Java中已經(jīng)棄用這種方式了)。
  • 可達(dá)性分析算法:從一個(gè)被稱為 GC Roots 的對(duì)象開(kāi)始向下搜索呢诬,如果一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí)涌哲,則說(shuō)明此對(duì)象不可用。

JDK1.2 之前尚镰,一個(gè)對(duì)象只有“已被引用”和"未被引用"兩種狀態(tài)阀圾,這將無(wú)法描述某些特殊情況下的對(duì)象,比如狗唉,當(dāng)內(nèi)存充足時(shí)需要保留初烘,而內(nèi)存緊張時(shí)才需要被拋棄的一類對(duì)象。

四種引用類型

所以在 JDK.1.2 之后,Java 對(duì)引用的概念進(jìn)行了擴(kuò)充账月,將引用分為了:強(qiáng)引用(Strong Reference)综膀、軟引用(Soft Reference)、弱引用(Weak Reference)局齿、虛引用(Phantom Reference)4 種剧劝,這 4 種引用的強(qiáng)度依次減弱。

一抓歼,強(qiáng)引用

Java中默認(rèn)聲明的就是強(qiáng)引用讥此,比如:

Object obj = new Object(); //只要obj還指向Object對(duì)象,Object對(duì)象就不會(huì)被回收
obj = null;  //手動(dòng)置null
復(fù)制代碼

只要強(qiáng)引用存在谣妻,垃圾回收器將永遠(yuǎn)不會(huì)回收被引用的對(duì)象萄喳,哪怕內(nèi)存不足時(shí),JVM也會(huì)直接拋出OutOfMemoryError蹋半,不會(huì)去回收他巨。如果想中斷強(qiáng)引用與對(duì)象之間的聯(lián)系,可以顯示的將強(qiáng)引用賦值為null减江,這樣一來(lái)染突,JVM就可以適時(shí)的回收對(duì)象了

二,軟引用

軟引用是用來(lái)描述一些非必需但仍有用的對(duì)象辈灼。在內(nèi)存足夠的時(shí)候份企,軟引用對(duì)象不會(huì)被回收,只有在內(nèi)存不足時(shí)巡莹,系統(tǒng)則會(huì)回收軟引用對(duì)象司志,如果回收了軟引用對(duì)象之后仍然沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常降宅。這種特性常常被用來(lái)實(shí)現(xiàn)緩存技術(shù)骂远,比如網(wǎng)頁(yè)緩存,圖片緩存等腰根。
在 JDK1.2 之后吧史,用java.lang.ref.SoftReference類來(lái)表示軟引用。

下面以一個(gè)例子來(lái)進(jìn)一步說(shuō)明強(qiáng)引用和軟引用的區(qū)別:
在運(yùn)行下面的Java代碼之前唠雕,需要先配置參數(shù) -Xms2M -Xmx3M,將 JVM 的初始內(nèi)存設(shè)為2M吨述,最大可用內(nèi)存為 3M岩睁。

首先先來(lái)測(cè)試一下強(qiáng)引用,在限制了 JVM 內(nèi)存的前提下揣云,下面的代碼運(yùn)行正常

public class TestOOM {

    public static void main(String[] args) {
         testStrongReference();
    }
    private static void testStrongReference() {
        // 當(dāng) new byte為 1M 時(shí)捕儒,程序運(yùn)行正常
        byte[] buff = new byte[1024 * 1024 * 1];
    }
}
復(fù)制代碼

但是如果我們將

byte[] buff = new byte[1024 * 1024 * 1];
復(fù)制代碼

替換為創(chuàng)建一個(gè)大小為 2M 的字節(jié)數(shù)組

byte[] buff = new byte[1024 * 1024 * 2];
復(fù)制代碼

則內(nèi)存不夠使用,程序直接報(bào)錯(cuò),強(qiáng)引用并不會(huì)被回收


接著來(lái)看一下軟引用會(huì)有什么不一樣刘莹,在下面的示例中連續(xù)創(chuàng)建了 10 個(gè)大小為 1M 的字節(jié)數(shù)組阎毅,并賦值給了軟引用,然后循環(huán)遍歷將這些對(duì)象打印出來(lái)点弯。

public class TestOOM {
    private static List<Object> list = new ArrayList<>();
    public static void main(String[] args) {
         testSoftReference();
    }
    private static void testSoftReference() {
        for (int i = 0; i < 10; i++) {
            byte[] buff = new byte[1024 * 1024];
            SoftReference<byte[]> sr = new SoftReference<>(buff);
            list.add(sr);
        }

        System.gc(); //主動(dòng)通知垃圾回收

        for(int i=0; i < list.size(); i++){
            Object obj = ((SoftReference) list.get(i)).get();
            System.out.println(obj);
        }

    }

}
復(fù)制代碼

打印結(jié)果:


我們發(fā)現(xiàn)無(wú)論循環(huán)創(chuàng)建多少個(gè)軟引用對(duì)象扇调,打印結(jié)果總是只有最后一個(gè)對(duì)象被保留,其他的obj全都被置空回收了抢肛。
這里就說(shuō)明了在內(nèi)存不足的情況下狼钮,軟引用將會(huì)被自動(dòng)回收。
值得注意的一點(diǎn) , 即使有 byte[] buff 引用指向?qū)ο? 且 buff 是一個(gè)strong reference, 但是 SoftReference sr 指向的對(duì)象仍然被回收了捡絮,這是因?yàn)镴ava的編譯器發(fā)現(xiàn)了在之后的代碼中, buff 已經(jīng)沒(méi)有被使用了, 所以自動(dòng)進(jìn)行了優(yōu)化熬芜。
如果我們將上面示例稍微修改一下:

    private static void testSoftReference() {
        byte[] buff = null;

        for (int i = 0; i < 10; i++) {
            buff = new byte[1024 * 1024];
            SoftReference<byte[]> sr = new SoftReference<>(buff);
            list.add(sr);
        }

        System.gc(); //主動(dòng)通知垃圾回收

        for(int i=0; i < list.size(); i++){
            Object obj = ((SoftReference) list.get(i)).get();
            System.out.println(obj);
        }

        System.out.println("buff: " + buff.toString());
    }
復(fù)制代碼

則 buff 會(huì)因?yàn)閺?qiáng)引用的存在,而無(wú)法被垃圾回收福稳,從而拋出OOM的錯(cuò)誤涎拉。


如果一個(gè)對(duì)象惟一剩下的引用是軟引用,那么該對(duì)象是軟可及的(softly reachable)的圆。垃圾收集器并不像其收集弱可及的對(duì)象一樣盡量地收集軟可及的對(duì)象鼓拧,相反,它只在真正 “需要” 內(nèi)存時(shí)才收集軟可及的對(duì)象略板。

三毁枯,弱引用

弱引用的引用強(qiáng)度比軟引用要更弱一些,無(wú)論內(nèi)存是否足夠叮称,只要 JVM 開(kāi)始進(jìn)行垃圾回收种玛,那些被弱引用關(guān)聯(lián)的對(duì)象都會(huì)被回收。在 JDK1.2 之后瓤檐,用 java.lang.ref.WeakReference 來(lái)表示弱引用赂韵。
我們以與軟引用同樣的方式來(lái)測(cè)試一下弱引用:

    private static void testWeakReference() {
        for (int i = 0; i < 10; i++) {
            byte[] buff = new byte[1024 * 1024];
            WeakReference<byte[]> sr = new WeakReference<>(buff);
            list.add(sr);
        }

        System.gc(); //主動(dòng)通知垃圾回收

        for(int i=0; i < list.size(); i++){
            Object obj = ((WeakReference) list.get(i)).get();
            System.out.println(obj);
        }
    }
復(fù)制代碼

打印結(jié)果:


可以發(fā)現(xiàn)所有被弱引用關(guān)聯(lián)的對(duì)象都被垃圾回收了。

四挠蛉,虛引用

虛引用是最弱的一種引用關(guān)系祭示,如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣谴古,它隨時(shí)可能會(huì)被回收质涛,在 JDK1.2 之后,用 PhantomReference 類來(lái)表示掰担,通過(guò)查看這個(gè)類的源碼汇陆,發(fā)現(xiàn)它只有一個(gè)構(gòu)造函數(shù)和一個(gè) get() 方法,而且它的 get() 方法僅僅是返回一個(gè)null带饱,也就是說(shuō)將永遠(yuǎn)無(wú)法通過(guò)虛引用來(lái)獲取對(duì)象毡代,虛引用必須要和 ReferenceQueue 引用隊(duì)列一起使用阅羹。

public class PhantomReference<T> extends Reference<T> {
    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
復(fù)制代碼

那么傳入它的構(gòu)造方法中的 ReferenceQueue 又是如何使用的呢?

五教寂,引用隊(duì)列(ReferenceQueue)

引用隊(duì)列可以與軟引用捏鱼、弱引用以及虛引用一起配合使用,當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí)酪耕,如果發(fā)現(xiàn)它還有引用导梆,那么就會(huì)在回收對(duì)象之前,把這個(gè)引用加入到與之關(guān)聯(lián)的引用隊(duì)列中去因妇。程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入了引用问潭,來(lái)判斷被引用的對(duì)象是否將要被垃圾回收,這樣就可以在對(duì)象被回收之前采取一些必要的措施婚被。

與軟引用狡忙、弱引用不同,虛引用必須和引用隊(duì)列一起使用址芯。

下面是博主剪輯的視頻資料 可能與文章無(wú)關(guān) 希望大家可以支持一下哦灾茁!謝謝大家支持!

UP主:我只需一小時(shí)帶你玩轉(zhuǎn)Git&Github B友直呼:菜雞這不是有手就行谷炸?

【面試必備】阿里資深架構(gòu)師詳解 2021最新 Java秒殺系統(tǒng)高性能高并發(fā)實(shí)戰(zhàn)項(xiàng)目

Java零基礎(chǔ)小白看完我這個(gè)系列視頻都可以自己做實(shí)戰(zhàn)項(xiàng)目啦北专!拿捏呢!

2021最新版lntellij IDEA 安裝旬陡、配置拓颓、環(huán)境變量教學(xué)

2021最新算法訓(xùn)練營(yíng):左神帶你 爆刷LeetCode算法(1000題) 進(jìn)大廠的必修算法課程!

B站首發(fā) 花費(fèi)12980巨資購(gòu)買的 微服務(wù)SpringCloud Alibaba全集

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末描孟,一起剝皮案震驚了整個(gè)濱河市驶睦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匿醒,老刑警劉巖场航,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異廉羔,居然都是意外死亡溉痢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門憋他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孩饼,“玉大人,你說(shuō)我怎么就攤上這事竹挡〉妨荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵此迅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)耸序,這世上最難降的妖魔是什么忍些? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮坎怪,結(jié)果婚禮上罢坝,老公的妹妹穿的比我還像新娘。我一直安慰自己搅窿,他們只是感情好嘁酿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著男应,像睡著了一般闹司。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沐飘,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天游桩,我揣著相機(jī)與錄音,去河邊找鬼耐朴。 笑死借卧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的筛峭。 我是一名探鬼主播铐刘,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼影晓!你這毒婦竟也來(lái)了镰吵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俯艰,失蹤者是張志新(化名)和其女友劉穎捡遍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體竹握,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡画株,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啦辐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谓传。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芹关,靈堂內(nèi)的尸體忽然破棺而出续挟,到底是詐尸還是另有隱情,我是刑警寧澤侥衬,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布诗祸,位于F島的核電站跑芳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏直颅。R本人自食惡果不足惜博个,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望功偿。 院中可真熱鬧盆佣,春花似錦、人聲如沸械荷。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吨瞎。三九已至痹兜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間关拒,已是汗流浹背佃蚜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留着绊,地道東北人谐算。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像归露,于是被迫代替她去往敵國(guó)和親洲脂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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