強引用(strong reference)
在一般的 Java 程序中,見到最多的就是強引用(strong reference)。如 Date date = newDate(),date 就是一個對象的強引用。對象的強引用可以在程序中到處傳遞。很多情況下,會同時有多個引用指向同一個對象。強引用的存在限制了對象在內存中的存活時間。假如對象 A 中包含了一個對象 B 的強引用,那麼一般情況下,對象 B 的存活時間就不會短於對象 A。如果對象 A 沒有顯式的把對象 B 的引用設爲 null 的話,就只有當對象 A 被垃圾回收之後,對象 B 纔不再有引用指向它,纔可能獲得被垃圾回收的機會。
軟引用(soft reference)
軟引用(soft reference)在強度上弱於強引用,通過類SoftReference來表示。它的作用是告訴垃圾回收器,程序中的哪些對象是不那麼重要,當內存不足的時候是可以被暫時回收的。當JVM中的內存不足的時候,垃圾回收器會釋放那 些只被軟引用所指向的對象。如果全部釋放完這些對象之後,內存還不足,纔會拋出OutOfMemory錯誤。軟引用非常適合於創建緩存。當系統內存不足的時候,緩存中的內容是可以被釋放的。比如考慮一個圖像編輯器的程序。該程序會把圖像文件的全部內容都讀取到內存中,以方便進行處理。而用戶也可以同時打開 多個文件。當同時打開的文件過多的時候,就可能造成內存不足。如果使用軟引用來指向圖像文件內容的話,垃圾回收器就可以在必要的時候回收掉這些內存。
import java.lang.ref.SoftReference;
public class ImageData {
private String path;
private SoftReference<byte[]> dataRef;
public ImageData(String path) {
this.path = path;
dataRef = new SoftReference<byte[]>(new byte[0]);
}
private byte[] readImage() {
return new byte[1024 * 1024]; // 省去了讀取文件的操作
}
public byte[] getData() {
byte[] dataArray = dataRef.get();
if (dataArray == null || dataArray.length == 0) {
dataArray = readImage();
dataRef = new SoftReference<byte[]>(dataArray);
}
return dataArray;
}
}
在運行上面程序的時候,可以使用 -Xmx 參數來限制JVM可用的內存。由於軟引用所指向的對象可能被回收掉,在通過 get方法來獲取軟引用所實際指向的對象的時候,總是要檢查該對象是否還存活。
弱引用(weak reference)
弱引用(weak reference)在強度上弱於軟引用,通過類 WeakReference來 表示。它的作用是引用一個對象,但是並不阻止該對象被回收。如果使用一個強引用的話,只要該引用存在,那麼被引用的對象是不能被回收的。弱引用則沒有這個問題。在垃圾回收器運行的時候,如果一個對象的所有引用都是弱引用的話,該對象會被回收。弱引用的作用在於解決強引用所帶來的對象之間在存活時間上的耦合關係。弱引用最常見的用處是在集合類中,尤其在哈希表中。哈希表的接口允許使用任何Java對象作爲鍵來使用。當一個鍵值對被放入到哈希表中之後,哈希表 對象本身就有了對這些鍵和值對象的引用。如果這種引用是強引用的話,那麼只要哈希表對象本身還存活,其中所包含的鍵和值對象是不會被回收的。如果某個存活 時間很長的哈希表中包含的鍵值對很多,最終就有可能消耗掉JVM中全部的內存。對於這種情況的解決辦法就是使用弱引用來引用這些對象,這樣哈希表中的鍵和值對象都能被垃圾回收。Java中提供了 WeakHashMap來滿足這一常見需求。
虛引用(phantom reference)
在介紹虛引用之前,要先介紹Java提供的 對象終止化機制(finalization)。在Object類裏面有個 finalize方法,其設計的初衷是在一個對象被真正回收之前,可以用來執行一些清理的工作。因爲 Java並沒有提供類似 C++的析構函數一樣的機制,就通過finalize方法來實現。但是問題在於垃圾回收器的運行時間是不固定的,所以這些清理工作的實際運行時間也是不能預知的。虛引用(phantom reference)可以解決這個問題。在創建虛引用 PhantomReference的時候必須要指定一個引用隊列。當一個對象的finalize方法已經被調用了之後,這個對象的虛引用會被加入到隊列中。通過檢查該隊列裏面的內容就知道一個對象是不是已經準備要被回收了。
在有些情況下,程序會需要在一個對象的可達到性發生變化的時候得到通知。比如某個對象的強引用都已經不存在了,只剩下軟引用或是弱引用。但是還需要對引用本身做一些的處理。典型的情景是在哈希表中。引用對象是作爲WeakHashMap中的鍵對象的,當其引用的實際對象被垃圾回收之後,就需要把該鍵值對從哈希表中刪除。有了引用隊列(ReferenceQueue),就可以方便的獲取到這些弱引用對象,將它們從表中刪除。在軟引用和弱引用對象被添加到隊列之前,其對實際對象的引用會被自動清空。通過引用隊列的 poll/remove方法就可以分別以非阻塞和阻塞的方式獲取隊列中的引用對象。
參考文獻:
深入理解JVM
Java深度探索