強引用、軟引用、弱引用、幻象引用再不理解就晚了

遛狗的程式設計師發表於2018-07-26

問題:

強引用、軟引用、弱引用、幻象引用有什麼區別?具體使用場景是什麼?

知識點:

  1. 物件可達性 物件可達性對於我們理解JVM 可達性分析有重要作用,具體後續文章會談到。
  2. 引用佇列(ReferenceQueue)使用。 利用引用佇列,我們可以在物件處於相應狀態時,執行後期處理邏輯。例如:LeakCanary監控記憶體洩漏的原始碼中:
//原始碼地址 LeakCanary
com.squareup.leakcanary.RefWatcher

private void removeWeaklyReachableReferences() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    KeyedWeakReference ref;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
    //do something
      retainedKeys.remove(ref.key);
    }
  }
複製程式碼

removeWeaklyReachableReferences 把已被回收的物件的 key 從 retainedKeys 移除,剩下的 key 都是未被回收的物件;LeakCanary分析可以參考文章:帶你學開源專案:LeakCanary- 如何檢測 Activity 是否洩漏 3. Reachablity Fence

除了這四種基本引用型別,我們也可以通過底層API來達到引用的效果,這就是所謂的設定reachability fence。這裡不做詳細介紹,有興趣的自己檢視相關資料。

回答問題:

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

強引用:我們正常new出來物件就是強引用,當記憶體不夠的時候,JVM寧可丟擲異常,也不會回收強引用物件。對於一個普通的物件,如果沒有其他的引用關係,只要超過了引用的作用域或者顯示的將相應(強)引用賦值為null,就是可以被垃圾收集了,當然具體回收時機還是要看垃圾收集策略。

軟引用(SoftReference):軟引用生命週期比強引用低,在記憶體不夠的時候,會進行回收軟引用物件。軟引用物件經常和引用佇列(ReferenceQueue)一起使用,在軟引用所引用的物件被GC回收後,會把該引用加入到引用佇列中。通常用來實現記憶體敏感的快取。例如:Android圖片框架Fresco中就使用到了。

public class OOMSoftReference<T> {

  SoftReference<T> softRef1;
  SoftReference<T> softRef2;
  SoftReference<T> softRef3;

  public OOMSoftReference() {
    softRef1 = null;
    softRef2 = null;
    softRef3 = null;
  }

  public void set(@Nonnull T hardReference) {
    softRef1 = new SoftReference<T>(hardReference);
    softRef2 = new SoftReference<T>(hardReference);
    softRef3 = new SoftReference<T>(hardReference);
  }

  @Nullable
  public T get() {
    return (softRef1 == null ? null : softRef1.get());
  }

  public void clear() {
    if (softRef1 != null) {
      softRef1.clear();
      softRef1 = null;
    }
    if (softRef2 != null) {
      softRef2.clear();
      softRef2 = null;
    }
    if (softRef3 != null) {
      softRef3.clear();
      softRef3 = null;
    }
  }
}
複製程式碼

有興趣可以自己去檢視原始碼。

弱引用(WeakReference):弱引用生命週期比軟引用要短,在下一次GC的時候,掃描到它所管轄的區域存在弱引用的話,不管當前記憶體是否夠,都會進行回收。(由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快回收弱引用的物件)。弱引用和軟引用一樣,也會經常和引用佇列(ReferenceQuene)一起使用,在弱引用所引用的物件被GC回收後,會把該引用加入到引用佇列中。經常用在圖片快取中。例如:ThreadLocal中所持用的靜態類ThreadLocalMap的Key值就用到了弱引用,防止記憶體洩露(value可能存在記憶體洩露,呼叫remove方法)。

/**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

複製程式碼

有興趣的話,可以檢視原始碼,也可以參考文章:理解Java中的ThreadLocal

幻象引用(PhantomReference):又叫虛引用,幻想引用僅僅是提供了一種確保物件被finalize後會處理某些事情,必須和引用佇列一起使用(ReferenceQuene)。比如上面一篇文章所說的Cleaner機制中就使用到了幻象引用,也可以用來跟蹤物件被垃圾回收器回收的活動,當一個幻象引用關聯的物件被垃圾回收器回收之前收到一條系統通知。

參考:

宣告:此為原創,轉載請聯絡作者


作者:微信公眾號新增公眾號-遛狗的程式設計師 ,或者可以掃描以下二維碼關注相關技術文章。

qrcode_for_gh_1ba0785324d6_430.jpg
當然喜愛技術,樂於分享的你也可以可以新增作者微訊號:

WXCD.jpeg

相關文章