Java中的四種引用類型

一、背景

??Java的內存回收不需要程序員負責,JVM會在必要時啟動Java GC完成垃圾回收。Java以便我們控制對象的生存周期,提供給了我們四種引用方式,引用強度從強到弱分別為:強引用、軟引用、弱引用、虛引用。

二、簡介

1.強引用 StrongReference

??StrongReference 是 Java 的默認引用形式,使用時不需要顯示定義。任何通過強引用所使用的對象不管系統資源有多緊張,Java GC都不會主動回收具有強引用的對象。

public class StrongReferenceTest {

    public static int M = 1024*1024;

    public static void printlnMemory(String tag){
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n"+tag+":");
        System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
    }
    
    public static void main(String[] args){
        StrongReferenceTest.printlnMemory("1.原可用內存和總內存");
        
        //實例化10M的數組并與strongReference建立強引用
        byte[] strongReference = new byte[10*StrongReferenceTest.M];
        StrongReferenceTest.printlnMemory("2.實例化10M的數組,并建立強引用");
        System.out.println("strongReference : "+strongReference);
        
        System.gc();
        StrongReferenceTest.printlnMemory("3.GC后");
        System.out.println("strongReference : "+strongReference);

        //strongReference = null;后,強引用斷開了
        strongReference = null;
        StrongReferenceTest.printlnMemory("4.強引用斷開后");
        System.out.println("strongReference : "+strongReference);
        
        System.gc();
        StrongReferenceTest.printlnMemory("5.GC后");
        System.out.println("strongReference : "+strongReference);
        }
}

運行結果:



2.弱引用 WeakReference

??如果一個對象只具有弱引用,無論內存充足與否,Java GC后對象如果只有弱引用將會被自動回收。

public class WeakReferenceTest {
    
    public static int M = 1024*1024;
    
    public static void printlnMemory(String tag){
        Runtime runtime = Runtime.getRuntime();
        int M = WeakReferenceTest.M;
        System.out.println("\n"+tag+":");
        System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
    }
    
    public static void main(String[] args){  
        WeakReferenceTest.printlnMemory("1.原可用內存和總內存");

        //創建弱引用
        WeakReference<Object> weakRerference = new WeakReference<Object>(new byte[10*WeakReferenceTest.M]);   
        WeakReferenceTest.printlnMemory("2.實例化10M的數組,并建立弱引用");
        System.out.println("weakRerference.get() : "+weakRerference.get());
        
        System.gc();
        StrongReferenceTest.printlnMemory("3.GC后");
        System.out.println("weakRerference.get() : "+weakRerference.get());
    }   
}

運行結果:



3.軟引用 SoftReference

??軟引用和弱引用的特性基本一致, 主要的區別在于軟引用在內存不足時才會被回收。如果一個對象只具有軟引用,Java GC在內存充足的時候不會回收它,內存不足時才會被回收。

public class SoftReferenceTest {
    
    public static int M = 1024*1024;
    
    public static void printlnMemory(String tag){
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n"+tag+":");
        System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
    }
    
    public static void main(String[] args){
        SoftReferenceTest.printlnMemory("1.原可用內存和總內存");
        
        //建立軟引用
        SoftReference<Object> softRerference = new SoftReference<Object>(new byte[10*SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("2.實例化10M的數組,并建立軟引用");
        System.out.println("softRerference.get() : "+softRerference.get());
      
        System.gc();  
        SoftReferenceTest.printlnMemory("3.內存可用容量充足,GC后");
        System.out.println("softRerference.get() : "+softRerference.get());  

        //實例化一個4M的數組,使內存不夠用,并建立軟引用
        //free=10M=4M+10M-4M,證明內存可用量不足時,GC后byte[10*m]被回收
        SoftReference<Object> softRerference2 = new SoftReference<Object>(new byte[4*SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("4.實例化一個4M的數組后");
        System.out.println("softRerference.get() : "+softRerference.get());
        System.out.println("softRerference2.get() : "+softRerference2.get());  
     } 
}

運行結果:



4.虛引用 PhantomReference

??從PhantomReference類的源代碼可以知道,它的get()方法無論何時返回的都只會是null。所以單獨使用虛引用時,沒有什么意義,需要和引用隊列ReferenceQueue類聯合使用。當執行Java GC時如果一個對象只有虛引用,就會把這個對象加入到與之關聯的ReferenceQueue中。

public class PhantomReferenceTest {

    public static int M = 1024*1024;

    public static void printlnMemory(String tag){
        Runtime runtime = Runtime.getRuntime();
        int M = PhantomReferenceTest.M;
        System.out.println("\n"+tag+":");
        System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
    }
    
    public static void main(String[] args) throws InterruptedException {
        
        PhantomReferenceTest.printlnMemory("1.原可用內存和總內存");
        byte[] object = new byte[10*PhantomReferenceTest.M];        
        PhantomReferenceTest.printlnMemory("2.實例化10M的數組后");
        
        //建立虛引用
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(object,referenceQueue);  
        
        PhantomReferenceTest.printlnMemory("3.建立虛引用后");
        System.out.println("phantomReference : "+phantomReference); 
        System.out.println("phantomReference.get() : "+phantomReference.get());
        System.out.println("referenceQueue.poll() : "+referenceQueue.poll());
        
        //斷開byte[10*PhantomReferenceTest.M]的強引用
        object = null;  
        PhantomReferenceTest.printlnMemory("4.執行object = null;強引用斷開后");
        
        System.gc();
        PhantomReferenceTest.printlnMemory("5.GC后");
        System.out.println("phantomReference : "+phantomReference); 
        System.out.println("phantomReference.get() : "+phantomReference.get());
        System.out.println("referenceQueue.poll() : "+referenceQueue.poll());       
       
        //斷開虛引用
        phantomReference = null;
        System.gc(); 
        PhantomReferenceTest.printlnMemory("6.斷開虛引用后GC");
        System.out.println("phantomReference : "+phantomReference);
        System.out.println("referenceQueue.poll() : "+referenceQueue.poll());           
    }
}

運行結果:



三、小結

??強引用是 Java 的默認引用形式,使用時不需要顯示定義,是我們平時最常使用到的引用方式。不管系統資源有多緊張,Java GC都不會主動回收具有強引用的對象。
??弱引用和軟引用一般在引用對象為非必需對象的時候使用。它們的區別是被弱引用關聯的對象在垃圾回收時總是會被回收,被軟引用關聯的對象只有在內存不足時才會被回收。
??虛引用的get()方法獲取的永遠是null,無法獲取對象實例。Java GC會把虛引用的對象放到引用隊列里面。可用來在對象被回收時做額外的一些資源清理或事物回滾等處理。
??由于無法從虛引獲取到引用對象的實例。它的使用情況比較特別,所以這里不把虛引用放入表格進行對比。這里對強引用、弱引用、軟引用進行對比:

引用類型 GC時JVM內存充足 GC時JVM內存不足
強引用 不被回收 不被回收
弱引用 被回收 被回收
軟引用 不被回收 被回收
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容