Java之"強引用、軟引用 和弱引用"
思考:Java中為何會有引用的概念?
思路:在Java裡,當一個物件M被建立的時候,它就被放在heap裡。當GC執行時,如果發現沒有任何引用指向物件M,M就會被回收,用以騰出記憶體空間。
總結:如果一個物件被回收,需要滿足兩個條件:
- 沒有任何引用指向它
- 觸發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來標記存活的物件,然後將某些不可達的物件和一些引用的物件進行回收,通過表格說明一下: