1. 程式人生 > >Java之"強引用、軟引用 和弱引用"

Java之"強引用、軟引用 和弱引用"

思考:Java中為何會有引用的概念?

思路:在Java裡,當一個物件M被建立的時候,它就被放在heap裡。當GC執行時,如果發現沒有任何引用指向物件M,M就會被回收,用以騰出記憶體空間。

總結:如果一個物件被回收,需要滿足兩個條件:

  1. 沒有任何引用指向它
  2. 觸發GC(Grabage Collection)

眾所周知,Java在不主動回收記憶體方面而優於C、C++等語言所以,有沒有什麼省心的主動觸發GC呢?當然有的--引用。

1、強引用(StrongReference)

定義:強引用是使用最普遍的引用。如果一個物件具有強引用,垃圾回收器絕對不會回收它。

例項程式碼(1):

public static void main(String[] args){
    Object object=new Book();
    System.gc();
}

執行結果:

<無任何結果>

說明:由於object物件是強引用,即使呼叫了垃圾回收,也不會回收object。當記憶體不足時,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會隨意回收具有強引用的物件來解決記憶體不足的問題。如果不使用時,可以通過如下方式來弱化引用:

object=null; //幫助垃圾回收器回收此物件

例項程式碼(2):

public void test(){
    Object object=new Object();
    ......
}

說明:在一個方法的內部有一個強引用,這個引用儲存在棧中,而真正的引用內容(Object)儲存在中。當這個方法執行完成後就會退出方法棧,則引用內容的引用不存在,這個Object物件就會被回收。

注意:如果這個object是個全域性變數時,就需要在不使用該物件時置為null,因為強引用不會被垃圾回收。

例項程式碼(3):回收時將陣列中的內容置為null,陣列物件為強引用。

private Object[] arr;
public void clear(){
    
    //let gc do the work
    for(int i=0;i<arr.length;i++){
        arr[i]=null;
    }

}

說明:arr陣列為強引用,呼叫清空陣列的方法將每個陣列內容賦值為null。不同於arr=null,arr的強引用仍然存在,避免在後續呼叫arr的add()等方法時重新分配記憶體。但是仍然可以將陣列中存放的引用型別清空,及時釋放記憶體。

2、軟引用(SoftReference)

理解:如果一個物件只具有軟引用,如果記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足,就會將這些物件的記憶體回收。只要它沒有被GC,該物件就可以被程式使用。

應用之一:用來實現記憶體敏感的快取記憶體

String value=new String("xxx");//強引用
SoftReference<String> softRefence=new SoftReference<String>(value);//軟引用

當記憶體不足時,可以理解為:

while(JVM.記憶體不足){
    value=null;    //轉為軟引用
    System.gc();
}

引用二:瀏覽器的後退按鈕的引用(當按後退按鈕時,上一個顯示的頁面內容是重新進行網路請求還是從快取中獲取?)

1、在當前頁面瀏覽結束時,就進行記憶體回收,那麼下次返回到當前頁面就需要重新進行網路請求

2、如果在當前頁面瀏覽結束時,將瀏覽內容放入記憶體,會造成大量的記憶體浪費,甚至會造成記憶體溢位

那麼軟引用的好處來了:(當瀏覽的快取達到一定的容量,就將物件回收)

Browser bro=new Browser();    //獲取的瀏覽內容物件
SoftReference soft=new SoftReference(bro);//在這之前新增邏輯判斷,瀏覽結束後,就將該物件置為軟引用

if(null != soft.get()){
    soft=(Browser)bro.get();//如果沒有被GC,直接複用
}else{
    bro=new Browser();    //重新構建,達到了記憶體複用
    soft=new SoftReference(bro);
}

軟引用和引用佇列(ReferenceQueue)聯合使用的情況:如果軟引用所引用的物件被垃圾回收器回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中(瞭解即可)。

3、弱引用

弱引用與軟引用的區別:具有弱引用的物件擁有更短的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了具有弱引用的物件,不管當前記憶體控制元件是否足夠,都會回收它的記憶體。由於垃圾回收執行緒的優先順序很低,因此不一定很快發現具有弱引用的物件。

例項程式碼:

public static void main(String[] args){
    String value=new String("xxx");
    WeakReference weak=new WeakReferenc(value);
    System.gc();
    }
}

執行結果:

obj [Date:13722379033165] is gc

結果說明:JVM垃圾回收時,弱引用被回收

執行結果等同於:

String value=new String("xxx");
//垃圾回收
if(JVM.垃圾回收){
    value=null;
    System.gc();
}

用例:如果這個物件只是偶爾的使用,而你希望在使用時能夠隨時獲取,但又不影響記憶體的垃圾回收,那麼應該使用WeakReference來標記此物件。

下面的程式碼會讓value再次變成強引用:

String str=weak.get();

4、虛引用

虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。(理解即可)

總結:

Java的四種引用的級別由高到低依次為:

強引用-->軟引用-->弱引用-->虛引用

    當垃圾回收器回收時,某些物件會被回收,某些物件不會被回收。垃圾回收器會從根物件Object來標記存活的物件,然後將某些不可達的物件和一些引用的物件進行回收,通過表格說明一下: