Java 引用類型

JDK1.2之后,java對(duì)引用的概念進(jìn)行了拓充,將引用分為強(qiáng)引用,軟引用,弱引用,虛引用

  1. 強(qiáng)引用: 指的是在代碼之中普遍存在的,類似Object obj = new Object() 這類的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象
  2. 軟引用: 用來(lái)描述一些還有用,但是并非重要的對(duì)象.對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中并進(jìn)行第二次回收.如果這次回收還是沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常.
  3. 弱飲用: 當(dāng)垃圾收集器工作時(shí),無(wú)論是否內(nèi)存足夠,都將回收掉只被若飲用關(guān)聯(lián)的對(duì)象
  4. 虛引用: 一個(gè)對(duì)象是否是有虛引用的存在,完全不會(huì)對(duì)其生成時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例.為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的是希望在其被收集器回收時(shí)收到一個(gè)系統(tǒng)通知.

強(qiáng)引用

JVM在GC的時(shí)候并不會(huì)釋放強(qiáng)引用的堆實(shí)例, 因此當(dāng)堆內(nèi)GC后仍然不能獲得足夠的空間, 就會(huì)發(fā)生OOM

String str = new String("Hi");

上面的例子中, 在棧中分配的str指向了堆中分配的String實(shí)例, 那么str引用就是這個(gè)實(shí)例的強(qiáng)引用.

保存在數(shù)組和集合中以及Map中的引用都都算是強(qiáng)引用.

軟引用

JVM在GC時(shí)不一定會(huì)釋放軟引用所引用的對(duì)象實(shí)例, 那什么時(shí)候會(huì)進(jìn)行釋放呢? 只有當(dāng)JVM發(fā)現(xiàn)堆內(nèi)存不足時(shí), 才會(huì)在GC時(shí)將軟引用的堆內(nèi)存釋放掉

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        SoftReference<User> softReference = new SoftReference<>(user, referenceQueue);
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + ref);
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());
    }
}

class User {
    public String name;
}

我們指定虛擬機(jī)參數(shù)-Xmx10M -Xms10M -XX:PrintGC, 運(yùn)行一下這個(gè)程序的結(jié)果為:

[GC (Allocation Failure)  2048K->836K(9728K), 0.0023890 secs]
Before GC :  testRef.User@404b9385
[GC (System.gc())  1145K->844K(9728K), 0.0013400 secs]
[Full GC (System.gc())  844K->750K(9728K), 0.0085260 secs]
After GC : testRef.User@404b9385
[GC (Allocation Failure)  788K->782K(9728K), 0.0003760 secs]
[GC (Allocation Failure)  782K->782K(9728K), 0.0002590 secs]
[Full GC (Allocation Failure)  782K->750K(9728K), 0.0043290 secs]
[GC (Allocation Failure)  750K->750K(9728K), 0.0004580 secs]
[Full GC (Allocation Failure)  750K->692K(9728K), 0.0079430 secs]
Changed : java.lang.ref.SoftReference@19366529
Alocate : null

我們?cè)跇?gòu)建SoftReference實(shí)例對(duì)象時(shí), 除了添加一個(gè)測(cè)試對(duì)象外, 還添加里一個(gè)ReferenceQueue實(shí)例對(duì)象, 當(dāng)對(duì)象的可達(dá)狀態(tài)發(fā)生改變時(shí), SoftReference就會(huì)移動(dòng)到ReferenceQueue隊(duì)列里. 從最后的Poll 這個(gè)輸出里我們可以看到, 已經(jīng)看不到這個(gè)對(duì)象了.

弱引用

弱引用是一種比軟飲用更加弱的引用, JVM在GC時(shí)只要發(fā)現(xiàn)弱引用, 都會(huì)對(duì)其引用的實(shí)例進(jìn)行回收

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        WeakReference<User> softReference = new WeakReference<>(user, referenceQueue);
        // 確定沒(méi)有強(qiáng)引用
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + ref);
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());

    }
}

class User {}

我們指定虛擬機(jī)參數(shù)-Xmx10M -Xms10M -XX:+PrintGC, 運(yùn)行一下這個(gè)程序的結(jié)果為:

[GC (Allocation Failure)  2048K->800K(9728K), 0.0031060 secs]
Before GC :  null
Changed : java.lang.ref.WeakReference@175fdc70[GC (System.gc())  1084K->824K(9728K), 0.0011480 secs]
[Full GC (System.gc())  824K->748K(9728K), 0.0088060 secs]

After GC : null
[GC (Allocation Failure)  807K->812K(9728K), 0.0010100 secs]
[GC (Allocation Failure)  812K->844K(9728K), 0.0004150 secs]
[Full GC (Allocation Failure)  844K->748K(9728K), 0.0090930 secs]
[GC (Allocation Failure)  748K->748K(9728K), 0.0003230 secs]
[Full GC (Allocation Failure)  748K->690K(9728K), 0.0082600 secs]
Alocate : null

如果WeakReference是保存在一個(gè)對(duì)象實(shí)例里面是什么情況呢?

import java.lang.ref.WeakReference;

public class TestSoft {
    public static void main(String[] args) throws InterruptedException {
        WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
        weakReferenceCache.cache = new WeakReference<>(new User());
        System.out.println("Before GC : " + weakReferenceCache.cache.get());
        System.gc();
        System.out.println("After GC : " + weakReferenceCache.cache.get());
        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate GC : " + weakReferenceCache.cache.get());
    }
}

class WeakReferenceCache {
    public WeakReference<User> cache;

}
class User {
}

同樣我們運(yùn)行一下看一下結(jié)果

Before GC : User@41629346
After GC : null
Alocate GC : null

確實(shí)是, 每次GC都將其回收掉了

我們?cè)賹?shí)驗(yàn)一下, 如果將其存進(jìn)一個(gè)列表里

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class TestSoft {
    public static void main(String[] args) throws InterruptedException {
        WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
        for (int i = 0; i < 10; i++) {
            WeakReference<User> softReference = new WeakReference<>(new User());
            weakReferenceCache.cache.add(softReference);
        }

        System.out.println("Before GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
        System.gc();
        System.out.println("After GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
    }
}

class WeakReferenceCache {
    public List<WeakReference<User>> cache = new ArrayList<>();

}
class User {
}

結(jié)果為

Before GC : 
User@6433a2
User@5910e440
User@6267c3bb
User@533ddba
User@246b179d
User@7a07c5b4
User@26a1ab54
User@3d646c37
User@41cf53f9
User@5a10411
After GC : 
null
null
null
null
null
null
null
null
null
null
Alocate GC : 
null
null
null
null
null
null
null
null
null
null

即使是存儲(chǔ)在數(shù)組里也一樣被回收掉了

虛引用

虛引用是所有引用類型中最弱的一個(gè), 一個(gè)被虛引用持有的對(duì)象跟沒(méi)有被持有的效果基本上是一樣的. 當(dāng)我們從虛引用中g(shù)et時(shí), 總會(huì)獲得一個(gè)空, 那既然如此還為什么要設(shè)計(jì)出一個(gè)這樣的引用呢? 因?yàn)樘撘帽仨毟粋€(gè)引用隊(duì)列, 我們可以將一些資源性的東西放到虛引用中執(zhí)行和記錄.

import java.lang.ref.*;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        PhantomReference<User> softReference = new PhantomReference<>(user, referenceQueue);
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + System.currentTimeMillis());
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + System.currentTimeMillis() + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());

    }
}

class User {}

我們指定虛擬機(jī)參數(shù)-Xmx30M -Xms30M -XX:+PrintGC, 運(yùn)行一下這個(gè)程序的結(jié)果為:

Before GC : 1462461362835 null
[GC (System.gc())  2806K->904K(29696K), 0.0033390 secs]
[Full GC (System.gc())  904K->779K(29696K), 0.0095950 secs]
Changed : 1462461362850
After GC : null
Alocate : null
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末录粱,一起剝皮案震驚了整個(gè)濱河市诅迷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹈矮,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡旬盯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)翎猛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)胖翰,“玉大人,你說(shuō)我怎么就攤上這事切厘∪龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵疫稿,是天一觀的道長(zhǎng)培他。 經(jīng)常有香客問(wèn)我,道長(zhǎng)遗座,這世上最難降的妖魔是什么舀凛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮途蒋,結(jié)果婚禮上猛遍,老公的妹妹穿的比我還像新娘。我一直安慰自己号坡,他們只是感情好懊烤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著筋帖,像睡著了一般奸晴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上日麸,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天寄啼,我揣著相機(jī)與錄音逮光,去河邊找鬼。 笑死墩划,一個(gè)胖子當(dāng)著我的面吹牛涕刚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乙帮,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼杜漠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了察净?” 一聲冷哼從身側(cè)響起驾茴,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氢卡,沒(méi)想到半個(gè)月后锈至,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡译秦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年峡捡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筑悴。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡们拙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阁吝,到底是詐尸還是另有隱情砚婆,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布突勇,位于F島的核電站射沟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏与境。R本人自食惡果不足惜验夯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摔刁。 院中可真熱鬧挥转,春花似錦、人聲如沸共屈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拗引。三九已至借宵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矾削,已是汗流浹背壤玫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工豁护, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欲间。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓楚里,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親猎贴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子班缎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355