探究Java中的引用

探究Java中的四種引用

從JDK1.2版本開始,Java把對象的引用分為四種級別,從而使程式能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。本篇就來詳細探究一下這四種引用的機制:

  • 強引用
  • 軟引用
  • 弱引用
  • 虛引用
  • 詳解ReferenceQueue與Reference

強引用

強引用是最普遍的引用,一般通過new關鍵字來創建出來的對象引用都屬於強引用,比如Object o = new Object()。

如果一個對象具有強引用,它就不會被垃圾回收器回收。即使當前記憶體空間不足,JVM也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程式異常終止。如果想中斷強引用和某個對象之間的關聯,可以顯式地將引用賦值為null,這樣一來的話,JVM在合適的時間就會回收該對象。

軟引用

在使用軟引用時,如果記憶體的空間足夠,軟引用就能繼續被使用,而不會被垃圾回收器回收;只有在記憶體空間不足時,軟引用才會被垃圾回收器回收。軟引用最長被用作記憶體敏感型的記憶體快取。

創建一個軟引用的程式碼示例:

1

SoftReference<String> soft = new SoftReference<>("World");

SoftReference類的結構如下:

1  2  3  4  5  6  7  8  9

java.lang.ref.SoftReference#SoftReference(T)    java.lang.ref.SoftReference#SoftReference(T, java.lang.ref.ReferenceQueue<? super T>)    java.lang.ref.SoftReference#get    java.lang.ref.SoftReference#clock    java.lang.ref.SoftReference#timestamp

  • 前兩個是構造函數,後面會詳細介紹ReferenceQueue;
  • get方法用於獲取這個軟引用所指向的對象,如果這個對象已經清除或者被GC收集,那麼就返回null;
  • clock屬性是一個static的long型時間戳,由GC執行緒進行GC的時候更新;
  • timestamp屬性也是一個時間戳,每次在調用get方法的時候會對其進行更新,JVM用這個屬性用於幫助收集和清理軟引用。

弱引用

如果一個對象只具有弱引用,當 JVM 進行垃圾回收時,只要GC執行緒檢測到了,無論當前記憶體空間是否充足,都會將其回收。不過由於垃圾回收器是一個優先順序較低的執行緒,所以並不一定能迅速發現弱引用對象。弱引用通常用於實現規範化映射。

創建一個弱引用的程式碼示例:

1

WeakReference<String> weakName = new WeakReference<String>("hello");

WeakReference類的結構如下:

1  2  3

java.lang.ref.WeakReference#WeakReference(T)    java.lang.ref.WeakReference#WeakReference(T, java.lang.ref.ReferenceQueue<? super T>)

只是提供了兩個構造函數,後一個構造函數傳入了一個ReferenceQueue對象。

虛引用

顧名思義,就是形同虛設的引用,如果一個對象僅持有虛引用,那麼它相當於沒有引用,在任何時候都可能被垃圾回收器回收。

創建虛引用的程式碼示例:

1  2  3

ReferenceQueue<String> queue = new ReferenceQueue<String>();    PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);

PhantomReference類的結構如下:

1  2  3

java.lang.ref.PhantomReference#PhantomReference(T, ReferenceQueue<? super T>)    java.lang.ref.PhantomReference#get

  • 只有一個帶有ReferenceQueue參數的構造函數,也就是虛引用一定要和引用隊列一起使用;
  • 同時其get方法也有點特殊,因為虛引用的引用對象相當於沒有引用,所以其get方法總是返回null。

詳解ReferenceQueue與Reference

引用隊列可以與軟引用、弱引用以及虛引用一起配合使用,當垃圾回收器準備回收一個對象時,如果發現它還有引用,那麼就會在回收對象之前,把這個引用加入到與之關聯的引用隊列中去。程式可以通過判斷引用隊列中是否已經加入了引用,來判斷被引用的對象是否將要被垃圾回收,這樣就可以在對象被回收之前採取一些必要的措施。

與軟引用、弱引用不同,虛引用必須和引用隊列一起使用。

ReferenceQueue實現了一個隊列的入隊(enqueue)和出隊(poll還有remove)操作,內部元素就是Reference。ReferenceQueue名義上是一個隊列,但內部並沒有實際的存儲結構,它的存儲是依賴於內部節點之間的關係來實現的。看一下ReferenceQueue實現中用到的屬性:

 1   2   3   4   5   6   7   8   9  10  11  12  13

  static ReferenceQueue<Object> NULL = new Null<>();      static ReferenceQueue<Object> ENQUEUED = new Null<>();          static private class Lock { };      private Lock lock = new Lock();      private volatile Reference<? extends T> head = null;      private long queueLength = 0;

其實就是一個增加了同步操作的鏈表的設計,通過head屬性來找到鏈表頭,每個鏈表節點,即Reference對象,都有一個next屬性來找到下一個節點。

剛才分析四種引用的時候看到,java.lang.ref.Reference 為 軟(soft)引用、弱(weak)引用、虛(phantom)引用的父類。那我們再來看一下Reference類的實現中用到的屬性:

 1   2   3   4   5   6   7   8   9  10  11  12  13

  private T referent;     /* Treated specially by GC */      volatile ReferenceQueue<? super T> queue;      volatile Reference next;      transient private Reference<T> discovered; /* used by VM */      static private class Lock { }      private static Lock lock = new Lock();      private static Reference<Object> pending = null;

Reference作為ReferenceQueue中的節點,定義了next屬性來指向下一個節點,referent為實際指向的對象,pending存儲等待被放入ReferenceQueue的引用對象;discovered表示要處理的下一個對象。

Reference類還定義了一個ReferenceHandler執行緒。

1  2  3  4  5

java.lang.ref.Reference.ReferenceHandler#ReferenceHandler    java.lang.ref.Reference.ReferenceHandler#ensureClassInitialized    java.lang.ref.Reference.ReferenceHandler#run

這個執行緒在Reference類的static的構造塊中啟動,並且被設置為最高優先順序和daemon狀態。此執行緒要做的事情就是不斷的檢查pending屬性是否為null,如果pending不為null,則將pending進行enqueue,否則執行緒進入wait狀態。

由此可見,pending是由jvm來賦值的,當Reference內部的referent對象的可達狀態改變時,jvm會將Reference對象放入pending鏈表。並且這裡enqueue的隊列是我們在初始化(構造函數)Reference對象時傳進來的queue,如果傳入了null( 實際使用的是ReferenceQueue.NULL ),則ReferenceHandler則不進行enqueue操作,所以只有非RefernceQueue.NULL的queue才會將Reference進行enqueue。

ReferenceQueue作為 JVM GC與上層Reference對象管理之間的一個消息傳遞方式,它使得我們可以對所監聽的對象引用可達發生變化時做一些處理。

 

關注我的公眾號,獲取更多關於面試、技術的文章及福利資源。

Dali王的技術部落格公眾號