Java四種引用類型

介紹

Java中提供了四種引用類型埠褪,分別如下:

  • StrongReference(強(qiáng)引用)
  • SoftReference(軟引用)
  • WeakReference(弱引用)
  • PhantomReference(虛引用)

其中StrongReference是包權(quán)限無法使用浓利,其它三種引用類型都是公共的可以在應(yīng)用中使用,下面是Reference的類結(jié)構(gòu)钞速。

image.png

StrongReference

Java中的強(qiáng)引用其實(shí)就是new對(duì)象贷掖,可以通過引用操作堆中的對(duì)象(和C中的指針類似),例如:

StringBuffer buffer = new StringBuffer("HelloWorld!");

變量buffer指向StringBuffer所在的堆空間渴语,通過buffer來進(jìn)行操作苹威。

image.png
StrongReference的特性

1.可以直接訪問目標(biāo)對(duì)象;
2.指向的對(duì)象不會(huì)被GC回收??遵班,當(dāng)JVM內(nèi)存不足時(shí)會(huì)拋出OOM異常終端程序屠升;
3.基于上面第2點(diǎn),當(dāng)其它需要釋放的代碼塊持StrongReference會(huì)造成內(nèi)存泄露狭郑;


SoftReference

SoftReference即軟引用腹暖,當(dāng)JVM堆空間使用率到達(dá)閾值的時(shí)候會(huì)觸發(fā)GC回收,我們可以用它來實(shí)現(xiàn)對(duì)內(nèi)存敏感的緩存翰萨,SoftReference的特性是可以保留對(duì)Java對(duì)象軟引用的實(shí)例脏答,軟引用的實(shí)例并不會(huì)阻止GC進(jìn)行回收,在GC線程進(jìn)行回收之前我們可以通過它的get方法獲取到對(duì)象的強(qiáng)引用亩鬼,一旦對(duì)象被GC回收后get方法將返回null殖告。

下面我們通過代碼來實(shí)踐,通過設(shè)置JVM堆內(nèi)存大小為2M來模擬:

java -Xms2M -Xmx2M -verbose:gc -XX:+PrintGCDetails [class文件]
  • -Xms2M
    堆大小固定為2M
  • -verbose:gc
    輸出虛擬機(jī)中GC的詳細(xì)情況
  • -XX:+PrintGCDetails
    在控制臺(tái)上打印出GC具體細(xì)節(jié)
    public static void testSoftRef() {
        SoftReference<byte[]> softRef1 = new SoftReference<>(new byte[1024 * 300]);
        SoftReference<byte[]> softRef2 = new SoftReference<>(new byte[1024 * 300]);
        SoftReference<byte[]> softRef3 = new SoftReference<>(new byte[1024 * 300]);
        SoftReference<byte[]> softRef4 = new SoftReference<>(new byte[1024 * 300]);
        SoftReference<byte[]> softRef5 = new SoftReference<>(new byte[1024 * 300]);

        System.out.println(softRef1.get());
        System.out.println(softRef2.get());
        System.out.println(softRef3.get());
        System.out.println(softRef4.get());
        System.out.println(softRef5.get());
    }

結(jié)果如下雳锋,我們可以看到上面分配的byte長度是超出年輕代大小的黄绩,當(dāng)內(nèi)存不足時(shí)的確觸發(fā)了GC進(jìn)行回收,正如上面所說的那樣玷过。

[GC (Allocation Failure) [PSYoungGen: 510K->320K(1024K)] 510K->320K(1536K), 0.0037082 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 825K->432K(1024K)] 825K->432K(1536K), 0.0016384 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 942K->496K(1024K)] 942K->512K(1536K), 0.0113468 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 984K->984K(1024K)] 1300K->1493K(1536K), 0.0014734 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 984K->34K(1024K)] [ParOldGen: 509K->493K(512K)] 1493K->527K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0131428 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 334K->334K(1024K)] [ParOldGen: 493K->492K(512K)] 827K->827K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0106514 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 334K->0K(1024K)] [ParOldGen: 492K->397K(512K)] 827K->397K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0042212 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 300K->332K(1024K)] 697K->729K(1536K), 0.0002648 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
null
null
null
[B@610455d6
[B@511d50c0
Heap
 PSYoungGen      total 1024K, used 646K [0x00000007bfe80000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 512K, 61% used [0x00000007bfe80000,0x00000007bfecea50,0x00000007bff00000)
  from space 512K, 64% used [0x00000007bff80000,0x00000007bffd3010,0x00000007c0000000)
  to   space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
 ParOldGen       total 512K, used 397K [0x00000007bfe00000, 0x00000007bfe80000, 0x00000007bfe80000)
  object space 512K, 77% used [0x00000007bfe00000,0x00000007bfe63570,0x00000007bfe80000)
 Metaspace       used 3291K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 363K, capacity 388K, committed 512K, reserved 1048576K

引用隊(duì)列(ReferenceQueue)

在很多場景下爽丹,我們的程序需要在一個(gè)對(duì)象的可達(dá)性(是否已經(jīng)被GC回收)發(fā)生變化時(shí)得到通知筑煮,引用隊(duì)列就是用于收集這些信息的隊(duì)列。在創(chuàng)建SoftReference對(duì)象時(shí)粤蝎,可以為其關(guān)聯(lián)一個(gè)引用隊(duì)列真仲,當(dāng)SoftReference所引用的對(duì)象被GC回收時(shí),Java虛擬機(jī)就會(huì)將該SoftReference對(duì)象添加到與之關(guān)聯(lián)的引用隊(duì)列中初澎。當(dāng)需要檢測這些通知信息時(shí)秸应,就可以從引用隊(duì)列中獲取這些SoftReference對(duì)象。不僅是SoftReference碑宴,下面介紹的弱引用和虛引用都可以關(guān)聯(lián)相應(yīng)的隊(duì)列软啼。

WeakReference

WeakReference即弱引用,當(dāng)觸發(fā)GC時(shí)延柠,無論JVM堆內(nèi)存是否足夠?qū)ο蠖紩?huì)被回收焰宣,下面進(jìn)行測試:

    public static void testWeakRef() {
        byte[] buffer = new byte[1024 * 500];
        WeakReference<byte[]> weakReference = new WeakReference<>(buffer);
        System.out.println("GC前:" + weakReference.get());
        buffer = null;
        //手動(dòng)觸發(fā)GC
        System.gc();
        System.out.println("GC后:" + weakReference.get());
    }

結(jié)果如下:

GC前:[B@610455d6
GC后:null

SoftReference和WeakReference都適用于保存可選的緩存數(shù)據(jù),在系統(tǒng)內(nèi)存不足時(shí)捕仔,將回收緩存的數(shù)據(jù)不會(huì)導(dǎo)致OOM,并且緩存也能存在很長一段時(shí)間盈罐。

PhantomReference

PhantomReference即虛引用榜跌,是所有類型中最弱的,它幾乎是沒有引用因?yàn)殡S時(shí)會(huì)被GC回收盅粪,當(dāng)調(diào)用它的get方法獲取強(qiáng)引用時(shí)始終都是返回null钓葫,它必須要和ReferenceQueue一起使用,用來跟蹤垃圾回收過程票顾。

當(dāng)GC要回收對(duì)象時(shí)础浮,如果發(fā)現(xiàn)PhantomReference后將進(jìn)行GC然后銷毀該對(duì)象,并將PhantomReference添加到ReferenceQueue中奠骄,判斷是否向ReferenceQueue添加了PhantomReference可以確定是否需要對(duì)引用的對(duì)象進(jìn)行回收豆同,如果ReferenceQueue中存在PhantomReference那么在回收引用對(duì)象之前可以進(jìn)行一些額外的操作。

    public static void testPhantomRef() {
        byte[] buffer = new byte[1024 * 500];
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<byte[]> phantomReference = new PhantomReference<>(buffer,referenceQueue);
        buffer = null;
        System.out.println("GC前:" + phantomReference.get());
        System.gc();
        System.out.println("GC后:" + phantomReference.get());
    }

結(jié)果如下:

GC前:null
GC后:null

關(guān)于PhantomReference的get方法總是返回null含鳞。

    /**
     * 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;
    }

WeakHashMap

顧名思義影锈,它和HashMap一樣都是實(shí)現(xiàn)了Map接口,只不過它使用的是WeakReference作為存儲(chǔ)蝉绷,WeakHashMap是典型的弱引用例子鸭廷。

Entry弱引用key,強(qiáng)引用value熔吗;當(dāng)不再由強(qiáng)引用指向key時(shí)辆床,則key可以被垃圾回收,當(dāng)key被垃圾回收之后桅狠,對(duì)應(yīng)的Entry對(duì)象會(huì)被Java虛擬機(jī)加入到其關(guān)聯(lián)的隊(duì)列中讼载。當(dāng)應(yīng)用程序下次操作WeakHashMap時(shí)轿秧,例如對(duì)WeakHashMap的擴(kuò)容操作,就會(huì)遍歷關(guān)聯(lián)的引用隊(duì)列维雇,將其中的Entry對(duì)象從WeakHashMap中刪除淤刃。

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V> {}

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {}

需要注意的是如果WeakHashMap的key在系統(tǒng)中是StrongReference強(qiáng)引用的, 那么WeakHashMap將退化為一個(gè)普通的HashMap,因?yàn)樗荒鼙籊C回收吱型。

image.png

參考資料

再談四種引用狀態(tài)

常用JVM命令參數(shù)

Do You Really Know the 4 Reference Types in Java?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逸贾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子津滞,更是在濱河造成了極大的恐慌铝侵,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件触徐,死亡現(xiàn)場離奇詭異咪鲜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)撞鹉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門疟丙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鸟雏,你說我怎么就攤上這事享郊。” “怎么了孝鹊?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵炊琉,是天一觀的道長。 經(jīng)常有香客問我又活,道長苔咪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任柳骄,我火速辦了婚禮团赏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夹界。我一直安慰自己馆里,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布可柿。 她就那樣靜靜地躺著鸠踪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪复斥。 梳的紋絲不亂的頭發(fā)上营密,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音目锭,去河邊找鬼评汰。 笑死纷捞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的被去。 我是一名探鬼主播主儡,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惨缆!你這毒婦竟也來了糜值?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤坯墨,失蹤者是張志新(化名)和其女友劉穎寂汇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捣染,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骄瓣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了耍攘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榕栏。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蕾各,靈堂內(nèi)的尸體忽然破棺而出臼膏,到底是詐尸還是另有隱情,我是刑警寧澤示损,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站嚷硫,受9級(jí)特大地震影響检访,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仔掸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一脆贵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧起暮,春花似錦卖氨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纸厉,卻和暖如春系吭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颗品。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工肯尺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沃缘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓则吟,卻偏偏與公主長得像槐臀,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氓仲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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

  • 在Java中提供了四個(gè)級(jí)別的引用:強(qiáng)引用水慨,軟引用,弱引用和虛引用寨昙。在這四個(gè)引用類型中讥巡,只有強(qiáng)引用FinalRefe...
    墨雨軒夏閱讀 396評(píng)論 0 1
  • java內(nèi)存管理分為內(nèi)存分配和內(nèi)存回收,都不需要程序員負(fù)責(zé)舔哪,垃圾回收的機(jī)制主要是看對(duì)象是否有引用指向該對(duì)象 jav...
    千手雨閱讀 505評(píng)論 0 0
  • 掌握J(rèn)ava中的引用類型欢顷,有助于我們理解對(duì)象的生命周期,以及如何改變或者介入對(duì)象生命周期中的各個(gè)階段捉蚤。 每種編程語...
    城外李桃園閱讀 232評(píng)論 0 0
  • Java中提供了四個(gè)級(jí)別的引用:強(qiáng)引用题暖、軟引用、弱引用捉超、虛引用胧卤,除強(qiáng)引用以外,其他的引用類型在java.lang....
    我有一只喵喵閱讀 559評(píng)論 0 1
  • Java垃圾回收機(jī)制可以用 3 個(gè)詞來概括: where拼岳, when 和 how枝誊? When: 對(duì)象何時(shí)需要被回收...
    六尺帳篷閱讀 2,064評(píng)論 0 7