Java 中的強引用、弱引用、軟引用和虛引用
一、簡介
當我們用 Java 編程時,我們經常使用硬引用,通常甚至都沒有考慮過——這是有充分理由的,因為它們是大多數情況下的最佳選擇。然而,有時我們需要更多地控制對象可用於垃圾收集器清除的時間。
在本文中,我們將探討硬引用類型和各種非硬引用類型之間的區別以及何時可以使用它們。
2.硬參考
硬(或強)引用是默認的引用類型,大多數時候,我們甚至可能沒有想過引用的對象何時以及如何被垃圾回收。如果可以通過任何強引用訪問該對象,則該對像不能被垃圾回收。假設我們創建了一個ArrayList
對象並將其分配給list
變量:
List<String> list = new ArrayList<>;
垃圾收集器無法收集這個列表,因為我們在list
變量中持有對它的強引用。但是如果我們然後使變量無效:
list = null;
現在,也只是現在,可以收集ArrayList
對象,因為沒有任何對象持有對它的引用。
3. 超越硬引用
硬引用是默認值是有充分理由的。它們讓垃圾收集器按預期工作,因此我們不必擔心管理內存分配。儘管如此,在某些情況下,即使我們仍然持有對這些對象的引用,我們仍希望收集對象並釋放內存。
4. 軟引用
軟引用告訴垃圾收集器可以根據收集器的判斷收集引用的對象。對象可以在內存中保留一段時間,直到收集器決定他需要收集它。這種情況會發生,尤其是當 JVM 面臨內存不足的風險時。在OutOfMemoryError
異常之前,應清除所有對只能通過軟引用訪問的對象的軟引用。
我們可以通過將它包裝在我們的對象周圍來輕鬆地使用軟引用:
SoftReference<List<String>> listReference = new SoftReference<List<String>>(new ArrayList<String>());
如果我們想要檢索引用對象,我們可以使用get
方法。因為該對象可能已經被清除,所以我們需要檢查它:
List<String> list = listReference.get();
if (list == null) {
// object was already cleared
}
4.1.用例
軟引用可用於使我們的代碼對與內存不足相關的錯誤更有彈性。例如,我們可以創建一個內存敏感的緩存,當內存不足時自動清除對象。我們不需要手動管理內存,因為垃圾收集器會為我們做這件事。
5.弱引用
不會阻止收集僅由弱引用引用的對象。從垃圾回收的角度來看,它們根本不可能存在。如果一個弱引用的對象應該被保護不被清除,它也應該被一些硬引用引用。
5.1.用例
弱引用最常用於創建規範化映射。這些是僅映射可以到達的對象的映射。一個很好的例子是WeakHashMap
,它像普通的HashMap
一樣工作,但它的鍵是弱引用的,當引用被清除時它們會自動刪除。
使用WeakHashMap
,我們可以創建一個短期緩存,用於清除代碼其他部分不再使用的對象。如果我們使用普通的HashMap,
那麼僅僅存在映射中的鍵就會阻止它被垃圾收集器清除。
6.幻影參考
與弱引用類似,幻象引用不會禁止垃圾收集器將對象排入隊列以進行清除。不同之處在於幻象引用必須在最終確定之前從引用隊列中手動輪詢。這意味著我們可以在它們被清除之前決定我們想做什麼。
6.1.用例
如果我們需要實現一些終結邏輯,幻影引用就非常有用,而且它們比finalize
方法更可靠、更靈活。讓我們編寫一個簡單的方法,它將遍歷引用隊列並對所有引用執行清理:
private static void clearReferences(ReferenceQueue queue) {
while (true) {
Reference reference = queue.poll();
if (reference == null) {
break; // no references to clear
}
cleanup(reference);
}
}
幻引用不允許我們使用get
方法檢索它們的引用對象。因此,通常的做法是使用我們自己的類來擴展PhantomReference
類,其中包含對清理邏輯很重要的信息。
幻象引用的其他重要用例是調試和內存洩漏檢測。即使我們不需要執行任何終結操作,我們也可以使用幻像引用來觀察哪些對象正在被釋放以及何時被釋放。
七、結論
在本文中,我們探討了硬引用和不同類型的非硬引用及其用例。我們了解到,軟引用可用於內存保護,弱引用可用於規範化映射,幻象引用可用於細粒度終結。