強引用、軟引用、弱引用與虛引用

weixin_33670713發表於2018-08-26

在Java語言中,除了基本資料型別外,其他的都是指向各類物件的物件引用;Java中根據其生命週期的長短,將引用分為4類。

一、各類引用的介紹

1 強引用

        我們平常典型編碼Object obj = new Object()中的obj就是強引用。通過關鍵字new建立的物件所關聯的引用就是強引用。 當JVM記憶體空間不足,JVM寧願丟擲OutOfMemoryError執行時錯誤(OOM),使程式異常終止,也不會靠隨意回收具有強引用的“存活”物件來解決記憶體不足的問題。對於一個普通的物件,如果沒有其他的引用關係,只要超過了引用的作用域或者顯式地將相應(強)引用賦值為 null,就是可以被垃圾收集的了,具體回收時機還是要看垃圾收集策略。

2 軟引用

        軟引用通過SoftReference類實現。 軟引用的生命週期比強引用短一些。只有當 JVM 認為記憶體不足時,才會去試圖回收軟引用指向的物件:即JVM 會確保在丟擲 OutOfMemoryError 之前,清理軟引用指向的物件。軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收器回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中。後續,我們可以呼叫ReferenceQueue的poll()方法來檢查是否有它所關心的物件被回收。如果佇列為空,將返回一個null,否則該方法返回佇列中前面的一個Reference物件。

應用場景:軟引用通常用來實現記憶體敏感的快取。如果還有空閒記憶體,就可以暫時保留快取,當記憶體不足時清理掉,這樣就保證了使用快取的同時,不會耗盡記憶體。

3 弱引用

        弱引用通過WeakReference類實現。 弱引用的生命週期比軟引用短。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快回收弱引用的物件。弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯的引用佇列中。

應用場景:弱應用同樣可用於記憶體敏感的快取。

4 虛引用

        虛引用也叫幻象引用,通過PhantomReference類來實現。無法通過虛引用訪問物件的任何屬性或函式。幻象引用僅僅是提供了一種確保物件被 finalize 以後,做某些事情的機制。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用必須和引用佇列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之關聯的引用佇列中。

ReferenceQueue queue = new ReferenceQueue ();

PhantomReference pr = new PhantomReference (object, queue);

程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要被垃圾回收。如果程式發現某個虛引用已經被加入到引用佇列,那麼就可以在所引用的物件的記憶體被回收之前採取一些程式行動。

應用場景:可用來跟蹤物件被垃圾回收器回收的活動,當一個虛引用關聯的物件被垃圾收集器回收之前會收到一條系統通知。

二、擴充套件

1.引用出現的根源

        由於GC記憶體回收的基本原理—GC回收記憶體本質上是回首物件,而目前比較流行的回收演算法是可達性分析演算法,從GC Roots開始按照一定的邏輯判斷一個物件是否可達,不可達的話就說明這個物件已死(除此之外另外一種常見的演算法就是引用計數法,但是這種演算法有個問題就是不能解決相互引用的問題)。

2.四種引用

        基於此Java向使用者提供了四種可用的引用,強引用、軟引用、弱引用與虛引用。同時還提供了一種不可被使用的引用—FinalReference,這個引用是和解構函式密切相關的)。

        強引用,開發者可以通過new的方式建立,其它的幾種引用Java提供了相應的類:SoftReference、WeakReference、PhantomReference。如果你去檢視原始碼你會發現,這個類實現的核心是Reference與ReferenceQueue(更通俗地說引用佇列)兩個類,而且這兩個類也特別的簡單。Reference類似一個連結串列結構,通過建立一個守護執行緒來執行對應引用的清除、Cleaner.clean(如果傳入的物件是該類的話)、以及引用的入隊操作(需要在建立引用的時候制定一個引用佇列);ReferenceQueue這是制定了引用佇列的一些具體操作,簡單的來說它也是一個連結串列結構,並提供了一些基本的連結串列操作)。

        而除了強引用外其它的都是繼承於此,通過這樣的類約束了引用的相關內容,便於和GC進行互動。

3.四種引用的區別

1:強引用是隻有當GC明確判斷該引用無效的時候才會回收相應的引用物件,即使丟擲OOME警告。

2:軟引用是當GC檢測到繼續建立物件會導致OOME的時候會進行一次垃圾回收,這次回收會講軟引用回收以防丟擲異常,根據這樣的特點該引用常用來被當作快取使用。

3:虛引用是哪些如果引用未被使用,就會在最近的一次GC的時候被回收。例如Java的TheardLocal與動態代理都是基於這樣的一個引用實現的,一般針對那些比較敏感的資料。

4:幻想引用是針對那些已經執行完解構函式之後,仍然需要在執行一些其它操作的物件:比如資源物件的關閉就可以用到這個引用。

相關文章