Java的四種引用之強弱軟虛

轉摘地址:http://www.cnblogs.com/zwbg/p/6196000.htmlhtml

在java中提供4個級別的引用:強引用、軟引用、弱引用和虛引用。除了強引用外,其餘3中引用都可以在java.lang.ref包中找到對應的類。開發人員能夠在應用程序中直接使用他們。java

1 強引用緩存

強引用就是程序中通常使用的引用類型,強引用的對象是可觸及的,不會被回收。相對的,軟引用、弱引用和虛引用的對象是軟可觸及的、弱可觸及的和虛可觸及的,在必定條件下,都是能夠被回收的。jvm

強引用示例:StringBuffer str = new StringBuffer("Hello world");ide

假設以上代碼是在函數體內運行的,那麼局部變量str將被分配在棧上,而對象StringBuffer實例被分配在堆上。局部變量str指向StringBuffer實例所在堆空間,經過str能夠操做該實例,那麼str就是StringBuffer實例的強引用。函數

此時,若是再運行一個賦值語句:this

StringBuffer str1 = str;.net

則str所指向的對象也將被str1所指向,同時在局部變量表上會分配空間存放str1變量。此時該StringBuffer實例就有兩個引用。對引用的「==」操做用於表示兩操做數所指向的堆空間地址是否相同,不表示兩操做數所指的對象是否相同。線程

強引用的特色:htm

強引用能夠直接訪問目標對象 強引用所指向的對象在任什麼時候候都不會被系統回收,虛擬機寧願拋出OOM異常,也不會回收強引用所指的對象。 強引用可能致使內存泄漏

2 軟引用----可被回收的引用

軟引用是比強引用弱一點的引用類型,一個對象只持有軟引用,那麼當堆空間不足時,就會被回收。軟引用使用java.lang.ref.SoftReference類型

package com.jvm; import java.lang.ref.SoftReference; public class SoftRef {   public static class User{     public User(int id,String name){       this.id = id;       this.name = name;     }     public int id;     public String name;     @Override     public String toString() {       return "[id="+String.valueOf(id)+",name="+name+"]";     }   }   public static void main(String[] args) {     User u = new User(1,"gim");     SoftReference<User> userSoftRef = new SoftReference<User>(u);//建立引用給定對象的新的軟引用     u = null; //去除強引用

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

    byte[] b = new byte[10249257];//分配7M左右,使內存空間緊張     System.gc();     System.out.println(userSoftRef.get());   } }

若是虛擬機不加參數設置堆內存限制,則打印出:

[id=1,name=gim] After GC: [id=1,name=gim] [id=1,name=gim]

由於虛擬機的內存總量是九十多兆,且虛擬機企圖使用的最大內存總量是這個的十幾倍。故不會出現資源緊張,也不會產生內存溢出現象。

若是使用-Xmx10m運行上述代碼,能夠獲得:

[id=1,name=gim](第一次從軟引用中獲取數據) After GC: [id=1,name=gim](GC沒有清除軟引用) null(因爲內存緊張,軟引用被清除)

結論:GC未必會回收軟引用的對象,可是當內存資源緊張時,軟引用對象會被回收,因此軟引用對象不會引發內存溢出。

3 弱引用-----發現即回收

弱引用是一種比軟引用較弱的引用類型。在系統GC時,只要發現弱引用,無論系統堆空間使用狀況如何,都會將對象進行回收。可是因爲垃圾回收器的優先級一般很低,所以,並不必定能很快的發現持有弱引用的對象。在這種狀況下,弱引用對象能夠存在較長的時間,一旦一個弱引用對象被垃圾回收器回收,便會加入一個註冊的引用隊列(這一點和軟引用很像)。弱引用使用java.lang.ref.WeakReference類實現。

實例2:展現弱引用的特色

package com.jvm; import java.lang.ref.WeakReference; public class WeakRef {   public static class User{     public User(int id,String name){       this.id = id;       this.name = name;     }     public int id;     public String name;

    @Override     public String toString() {       return "[id="+String.valueOf(id)+",name="+name+"]";     }   }

  public static void main(String[] args) {     User u = new User(1,"gim");     WeakReference<User> userReference = new WeakReference<User>(u);     u = null;     System.out.println(userReference.get());     System.gc();     System.out.println("After GC:");     System.out.println(userReference.get());   } }

不用設置虛擬機參數,直接運行代碼打印出:

[id=1,name=gim] After GC: null

能夠看書,GC以後,弱引用對象當即被清除。弱引用和軟引用同樣,在構造弱引用時,也能夠指定一個引用隊列,當弱引用對象被回收時,就會加入指定的引用隊列,經過這個隊列能夠跟蹤對象的回收狀況。

注意:軟引用、弱引用都很是適合來保存那些無關緊要的緩存數據(應用場景)。若是這麼作,當系統內存不足時,這些緩存數據會被回收,不會致使內存的溢出,而當內存資源充足時,這些緩存又能夠存在至關長的時間,從而起到加速系統的做用。

4 虛引用----對象回收跟蹤

一個持有虛引用的對象,和沒有引用幾乎同樣。隨時均可能被垃圾回收器回收,當試圖經過虛引用的get()方法取得強引用時,老是會失敗。

而且,虛引用必須和引用隊列一塊兒使用,它的做用主要是跟蹤垃圾回收過程

當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象後,將這個虛引用加入引用隊列,以通知引用程序對象的回收狀況。

實例3 :使用虛引用跟蹤一個可復活對象的回收

package com.jvm;

import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;

public class TraceCanReliveObj {   public static TraceCanReliveObj obj; //能夠復活對象   static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;   public static class CheckRefQueue extends Thread{     @Override     public void run() {       while(true){         if(phantomQueue!=null){           PhantomReference<TraceCanReliveObj> objt = null;           try{             objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove();           }catch(InterruptedException e){             e.printStackTrace();           }           if(objt!=null){             System.out.println("TraceCanReliveObj is delete by GC");           }         }       }     }   }

  @Override   protected void finalize() throws Throwable {     super.finalize();     System.out.println("CanReliveObj finalize called");     obj = this; //使得對象復活   }   @Override   public String toString() {     return "I am CanReliveObj";   }   public static void main(String[] args) throws InterruptedException {     Thread t = new CheckRefQueue();     t.setDaemon(true);//守護線程     t.start();     phantomQueue = new ReferenceQueue<TraceCanReliveObj>();     obj = new TraceCanReliveObj();

    //構造了TraceCanReliveObj對象的虛引用,並指定了引用隊列。     PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue);     obj = null; //將強引用去除     System.gc(); //第一次進行GC,因爲對象可復活,GC沒法回收該對象。     Thread.sleep(1000);     if(obj==null){       System.out.println("obj 是 null");     }else{       System.out.println("obj 可用");     }     System.out.println("第二次GC");     obj = null;     System.gc(); //第二次進行GC,因爲finalize()只會被調用一次,所以第2次GC會回收對象,同時其引用隊列應該也會捕獲到對象的回收     Thread.sleep(1000);     if(obj==null){       System.out.println("obj 是 null");     }else{       System.out.println("obj 可用");     }   } }

直接運行代碼打印:

CanReliveObj finalize called (對象復活) obj 可用 第二次GC           (第2次,對象沒法復活)  TraceCanReliveObj is delete by GC (引用隊列捕獲到對象被回收) obj 是 null

因爲虛引用能夠跟蹤對象的回收時間,所以,也能夠將一些資源釋放操做放置在虛引用中執行和記錄。

相關文章
相關標籤/搜索