1. 程式人生 > >基礎 | Java中四種引用的區別

基礎 | Java中四種引用的區別

在JDK 1.2以前,Java中的引用定義為:如果引用型別的資料中儲存的數值代表的是另一塊記憶體的起始地址,則這塊記憶體即代表著一個引用。故在該定義下,一個Java物件僅有被應用和未被引用兩種狀態。

在JDK 1.2之後,Java對引用的概念進行了擴充,將引用分為強引用、軟引用、弱引用和虛引用。這四種引用與Java虛擬機器的垃圾回收機制緊密關聯,建議重點關注。


Java中四種引用有什麼區別?

參考答案:

Java將引用分為強引用、軟引用、弱引用和虛引用四種,這四種引用強度依次逐漸減弱。

下面分別從四種引用的基本概念、具體實現、生命週期和應用場景四個方面進行對比分析。

強引用(Strong Reference):

  • 基本概念:在程式程式碼中普遍存在的,類似於“Object obj = new Object()”形式的引用。
  • 具體實現:建立一個物件並將其賦值給一個引用變數。
  • 生命週期:垃圾收集器永遠不會回收掉強引用所關聯著的物件,除非將該物件的所有引用全部置為null。
  • 應用場景:用的太多,以至於我說不出來(撒手狀)。

軟引用(Soft Reference):

  • 基本概念:描述一些還有用,但並非必需的物件。
  • 具體實現:JDK提供了SoftReference類來實現。
  • 生命週期:在系統將要發生記憶體溢位異常之前,將會把軟引用所關聯著的物件列進回收範圍內並進行第二次回收,若回收後仍沒有足夠記憶體則丟擲記憶體溢位異常。
  • 應用場景:用於實現記憶體敏感的快取記憶體,如網頁快取和圖片快取等。

注意:使用軟引用能防止記憶體洩漏,增強程式的健壯性。

弱引用(Weak Reference):

  • 基本概念:也描述非必需物件,強度比軟引用更弱。
  • 具體實現:JDK提供了WeakReference類來實現。
  • 生命週期:無論系統記憶體是否足夠,垃圾收集器工作時都會受到只被弱引用關聯著的物件。
  • 應用場景:多將物件的弱引用作為HashMap的key值,以實現在不需要該物件時,只需要在程式中將其強引用置為null,而無需手動將其從HashMap中移除(由垃圾收集器實現,WeakHashMap即是基於該原理)。

虛引用(Phantom Reference):

  • 基本概念:也稱幽靈引用或幻影引用,是最弱的一種引用關係。
  • 具體實現:JDK提供了PhantomReference類來實現。
  • 生命週期:虛引用並不會影響其所關聯物件的生存時間,也無法通過虛引用來取得一個物件例項。
  • 應用場景:虛引用可以在物件被收集器回收時收到一個系統通知,即多用於在物件銷燬前執行一些操作,如資源釋放等。

擴充套件程式碼閱讀

軟引用的具體使用:

// 建立軟引用並丟棄強引用
MyObject obj = new MyObject();
SoftReference softRef = new SoftReference(obj);
obj = null; //丟棄強引用

// 通過軟應用獲取強引用
// 若軟引用所關聯的物件未被回收則重新獲得強引用
// 若已被回收,則anotherObj = null
MyObject anotherObj = (MyObject)softRef.get();

/**
 * 若get()返回null,則說明軟引用所關聯的物件obj已被回收
 * softRef物件不再具有存在的價值
 * 可使用ReferenceQueue來將其清除,以避免發生記憶體洩漏
 */
MyObject obj = new MyObject();
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(obj, queue);
obj = null;

// 若ref所關聯的物件被回收,則ref會自動新增到queue佇列中去。

弱引用的具體使用:與軟引用雷同,其可以和一個引用對列(ReferenceQueue)聯合使用,如果弱引用所關聯的物件被JVM回收,該弱引用就會被新增到與之關聯的引用對列中。

虛引用的具體使用:必須和引用對列關聯使用,具體流程如下:

  • 對於虛引用所關聯的物件,垃圾收集器工作時會將該虛引用新增到與之關聯的引用對列中;
  • 程式通過判斷引用對列中是否已經加入了虛引用,來了解被引用物件是否將要被回收;
  • 若將要被回收,則可以在其被回收之前採取必要的操作。

虛引用的測試程式碼如下:

Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue);
obj=null;

while(true){
    System.out.printf("pf.get() = %d, isEnqueued: %b\r\n", pf.get(), pf.isEnqueued());
    if(pf.isEnqueued())
        break;
    System.gc();
    Thread.sleep(1000);                
}

// pf.get() = null, isEnqueued: false
// pf.get() = null, isEnqueued: true

擴充套件面試題

問:Java擴充套件為四種引用的目的是什麼?

  • 有利於Java虛擬機器進行垃圾回收;
  • 可以讓程式設計師通過程式碼的方式來決定某些物件的生命週期。

推薦閱讀


歡迎關注

Java名企面試吧,每天10點24分,我們不見不散!

丙子先生的宗旨是,每天以短篇幅講高頻面試題,不增加太多負擔,但需要持之以恆。

能力有限,歡迎指教!