java的強引用、軟引用、弱引用、幻象引用,引用佇列總結

NewHongjay發表於2018-11-30

java的強引用、軟引用、弱引用、幻象引用,引用佇列總結

  • java除了原始資料型別的變數,其他所有都是引用型別。
  • 引用分為強引用、軟引用、弱引用、幻象引用,這幾種引用影響著物件的回收

強引用

  • 強引用:形如Object object = new Object();這樣就是典型的強引用,被強引用引用的物件不會被垃圾收集器主動回收,JVM寧願丟擲OutOfMemoryError執行時錯誤(OOM),使程式異常終止,也不會靠隨意回收具有強引用的“存活”物件來解決記憶體不足的問題。對於一個普通的物件,如果沒有其他的引用關係,只要超過了引用的作用域或者顯式地將相應強引用賦值為 null,這個被引用的物件就是可以被垃圾回收器回收的(具體回收時機還是要看垃圾收集策略)。

引用(Reference類)

  • 先在這裡說一下,軟引用(SoftReference)、弱引用(WeakReference)、幻象引用(PhantomReference)都是java.lang.ref.Reference的子類,這個Reference類主要有4個方法
    • void clean();清除此參考物件。(此方法僅由Java程式碼呼叫; 當垃圾收集器清除引用時,它直接執行,而不呼叫此方法。)
    • boolean enqueue();將此引用物件新增到其註冊的佇列(如果有)。
    • T get();返回此引用物件的指示。(通過這個方法可以返回Reference所引用的物件,可以重新變成強引用) 例如:軟引用引用的一個物件
MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
aRef = null;
//現在只有一個軟引用指向MyObject的這個物件,
//如果這個物件還沒有被回收,可以把他再次變為強引用
if(aSoftRef.get() != null)
  MyObject bRef = aSoftRef.get();
//這個時候MyObject這個物件又變成強引用
複製程式碼
  • boolean isEnqueued();通過程式或垃圾收集器來告知這個引用物件是否已經入隊;

  • 其中enqueue 和 isEnqueued 這兩個方法涉及到引用佇列,我們後面會講到。這裡就先不解釋,留個印象就行。

軟引用(SoftReference)

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

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

弱引用(WeakReference)

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

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

幻象引用(PhantomReference)

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

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

引用佇列(ReferenceQueue)

  • Reference物件已經不再具有存在的價值,需要一個適當的清除機制,避免大量SoftReference物件帶來的記憶體洩漏。在java.lang.ref包裡還提供了ReferenceQueue。
  • 前面說到在使用軟引用、虛引用、幻象引用的時候可以指定一個引用佇列,在引用所引用的物件被回收後引用本身就會進入引用佇列。
    • 使用例子如下
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(aMyObject,queue);
複製程式碼
  • 通過引用佇列可以看到哪些Reference物件所引用的物件已經被回收,當呼叫引用佇列的poll()方法就可以返回除佇列中的失去所引用物件的Reference物件
  • 利用這個方法,我們可以檢查哪個SoftReference所軟引用的物件已經被回收。於是我們可以把這些失去所軟引用的物件的SoftReference物件清除掉。
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}
複製程式碼

一些應用

  • 軟引用和弱引用可以用來做一些記憶體敏感的快取,空間足夠的時候就快取物件,不夠的時候就回收,不會丟擲ome(記憶體溢位異常,網上有很多這種小demo,我這裡就不贅述了)
  • entry的key是軟引用從他的建構函式可以看出,為什麼是軟引用呢,因為當threadlocal物件不在使用的時候將其置位null,但是這個時候entry的key還是指向的threadlocal物件,如果這個時候是強引用就會導致threadlocal物件沒辦法回收會造成記憶體洩漏,所以改成弱引用的話當只有一個弱引用的entry的key指向threadlocal物件的時候Threadlocal物件在垃圾回收的時候就會被回收掉。(這裡涉及一些ThreadLocal的底層實現,大家可以看我這一篇部落格(https://juejin.im/post/5b98f3dc6fb9a05cd24d7cfb))

在強引用、軟引用、弱引用、幻象引用的介紹我引用了一些其他部落格下的評論,由於不好貼連結就只能宣告一下吧。

相關文章