java中的四種引用類型

 

對象的強、軟、弱和虛引用

  在JDK 1.2之前的版本中,若一個對象不被任何變量引用,那麼程序就沒法再使用這個對象。也就是說,只有對象處於可觸及(reachable)狀態,程序才能使用它。從JDK 1.2版本開始,把對象的引用分爲4種級別,從而使程序能更加靈活地控制對象的生命週期。這4種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。html

  

強引用(StrongReference)

  強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題。 java

   ps:強引用其實也就是咱們平時A a = new A()這個意思。linux

 

軟引用(SoftReference)

  軟引用(soft reference)在強度上弱於強引用,經過類SoftReference來表示。它的做用是告訴垃圾回收器,程序中的哪些對象是不那麼重要,當內存不足的時候是能夠被暫時回收的。當JVM中的內存不足的時候,垃圾回收器會釋放那些只被軟引用所指向的對象。若是所有釋放完這些對象以後,內存還不足,纔會拋出OutOfMemory錯誤。軟引用很是適合於建立緩存。當系統內存不足的時候,緩存中的內容是能夠被釋放的。api

  好比考慮一個圖像編輯器的程序。該程序會把圖像文件的所有內容都讀取到內存中,以方便進行處理。而用戶也能夠同時打開多個文件。當同時打開的文件過多的時候,就可能形成內存不足。若是使用軟引用來指向圖像文件內容的話,垃圾回收器就能夠在必要的時候回收掉這些內存。數組

 

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方法來獲取軟引用所實際指向的對象的時候,老是要檢查該對象是否還存活。緩存

弱引用(WeakReference)  

  在強度上弱於軟引用,經過類WeakReference來表示。它的做用是引用一個對象,可是並不阻止該對象被回收。若是使用一個強引用的話,只要該引用存在,那麼被引用的對象是不能被回收的。弱引用則沒有這個問題。在垃圾回收器運行的時候,若是一個對象的全部引用都是弱引用的話,該對象會被回收。弱引用的做用在於解決強引用所帶來的對象之間在存活時間上的耦合關係。弱引用最多見的用處是在集合類中,尤爲在哈希表中。哈希表的接口容許使用任何Java對象做爲鍵來使用。當一個鍵值對被放入到哈希表中以後,哈希表對象自己就有了對這些鍵和值對象的引用。若是這種引用是強引用的話,那麼只要哈希表對象自己還存活,其中所包含的鍵和值對象是不會被回收的。若是某個存活時間很長的哈希表中包含的鍵值對不少,最終就有可能消耗掉JVM中所有的內存。oracle

  對於這種狀況的解決辦法就是使用弱引用來引用這些對象,這樣哈希表中的鍵和值對象都能被垃圾回收。Java中提供了WeakHashMap來知足這一常見需求。編輯器

 

幽靈引用(PhantomReference)

  在介紹幽靈引用以前,要先介紹Java提供的對象終止化機制(finalization)。在Object類裏面有個finalize方法,其設計的初衷是在一個對象被真正回收以前,能夠用來執行一些清理的工做。由於Java並無提供相似C++的析構函數同樣的機制,就經過 finalize方法來實現。可是問題在於垃圾回收器的運行時間是不固定的,因此這些清理工做的實際運行時間也是不能預知的。幽靈引用(phantom reference)能夠解決這個問題。在建立幽靈引用PhantomReference的時候必需要指定一個引用隊列。當一個對象的finalize方法已經被調用了以後,這個對象的幽靈引用會被加入到隊列中。經過檢查該隊列裏面的內容就知道一個對象是否是已經準備要被回收了。函數

  幽靈引用及其隊列的使用狀況並很少見,主要用來實現比較精細的內存使用控制,這對於移動設備來講是頗有意義的。程序能夠在肯定一個對象要被回收以後,再申請內存建立新的對象。經過這種方式可使得程序所消耗的內存維持在一個相對較低的數量。好比下面的代碼給出了一個緩衝區的實現示例。this

public class PhantomBuffer {
  private byte[] data = new byte[0];
    private ReferenceQueue<byte[]> queue = new ReferenceQueue<byte[]>();
    private PhantomReference<byte[]> ref = new PhantomReference<byte[]>(data, queue);
    public byte[] get(int size) {
        if (size <= 0) {
            throw new IllegalArgumentException("Wrong buffer size");
        }
        if (data.length < size) {
            data = null;
            System.gc(); //強制運行垃圾回收器
             try {
                queue.remove(); //該方法會阻塞直到隊列非空
                ref.clear(); //幽靈引用不會自動清空,要手動運行
                ref = null;
                data = new byte[size];
                ref = new PhantomReference<byte[]>(data, queue);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
       }
       return data;
    }
}

 

  在上面的代碼中,每次申請新的緩衝區的時候,都首先確保以前的緩衝區的字節數組已經被成功回收。引用隊列的remove方法會阻塞直到新的幽靈引用被加入到隊列中。不過須要注意的是,這種作法會致使垃圾回收器被運行的次數過多,可能會形成程序的吞吐量太低。

 

 

引用隊列

  在有些狀況下,程序會須要在一個對象的可達到性發生變化的時候獲得通知。好比某個對象的強引用都已經不存在了,只剩下軟引用或是弱引用。可是還須要對引用自己作一些的處理。典型的情景是在哈希表中。引用對象是做爲WeakHashMap中的鍵對象的,當其引用的實際對象被垃圾回收以後,就須要把該鍵值對從哈希表中刪除。有了引用隊列(ReferenceQueue),就能夠方便的獲取到這些弱引用對象,將它們從表中刪除。在軟引用和弱引用對象被添加到隊列以前,其對實際對象的引用會被自動清空。經過引用隊列的poll/remove方法就能夠分別以非阻塞和阻塞的方式獲取隊列中的引用對象

相關文章
相關標籤/搜索