Java WeakHashMap指南

1.概述

在本文中,我們將從java.util包中查看*WeakHashMap* 。

為了理解數據結構,我們將在這裡使用它來推出簡單的緩存實現。但是,請記住,這是為了了解地圖的工作原理,並且創建自己的緩存實現幾乎總是一個壞主意。

簡而言之, WeakHashMapMap接口的基於哈希表的實現,其鍵為WeakReference類型。

如果WeakHashMap中的條目不再是普通使用的鍵,則該條目將被自動刪除,這意味著沒有單個*Reference指向該鍵。當垃圾回收(GC)進程丟棄某個鍵時,其條目將有效地從映射中刪除,因此此類的行為與其他Map*實現有所不同。

2.Strong,Soft和Weak引用

要了解WeakHashMap的工作原理,我們需要查看WeakReference-這是WeakHashMap實現中鍵的基本構造。在Java中,我們有三種主要的引用類型,我們將在以下各節中進行解釋。

2.1。Strong References強引用

強引用是我們在日常編程中使用的最常見的引用類型:

Integer prime = 1;

變量prime具有對值為1的Integer對象的強引用。任何指向其強引用的對象均不符合GC條件。

2.2。Soft References软引用

簡而言之,在JVM如果內存不足時,會對指向其*SoftReference*的對象進行垃圾回收;內存空間足夠, 垃圾回收期間不會回收它。

讓我們看看如何在Java中創建SoftReference

Integer prime = 1;

 SoftReference<Integer> soft = new SoftReference<Integer>(prime);

 prime = null;

首先prime對象強引用指向值爲1的對象。

接下來,我們將prime強引用包裝為軟引用。將強引用設為null之後對象可以使用GC,但僅在JVM絕對需要內存時才收集該對象。

2.3。Weak References弱引用

僅弱引用所引用的對象會被緊急收集;在這種情況下,GC不會等到需要內存。

我們可以通過以下方式在Java中創建WeakReference

Integer prime = 1;

 WeakReference<Integer> soft = new WeakReference<Integer>(prime);

 prime = null;

當我們將prime引用設為null時prime對象將在下一個GC週期中被垃圾回收,因為沒有其他強引用指向該對象。

WeakReference類型的引用WeakHashMap中用作鍵。

3. WeakHashMap作為高效的內存緩存

假設我們要建立一個緩存,將大圖像對象保留為值,並將圖像名稱保留為鍵。我們想選擇一個合適的map實現來解決該問題。

使用簡單的HashMap並不是一個好的選擇,因為值對象可能會佔用大量內存。而且,即使它們不再在我們的應用程序中使用,它們也永遠不會被GC進程從緩存中回收。

理想情況下,我們需要一個Map實現,該實現允許GC自動刪除未使用的對象。當我們的應用程序中的任何地方都沒有使用大圖像對象的鍵時,該條目將從內存中刪除。

幸運的是, WeakHashMap具有這些特徵。讓我們測試一下WeakHashMap並觀察其行為:

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();

 BigImage bigImage = new BigImage("image_id");

 UniqueImageName imageName = new UniqueImageName("name_of_big_image");



 map.put(imageName, bigImage);

 assertTrue(map.containsKey(imageName));



 imageName = null;

 System.gc();



 await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

我們正在創建一個WeakHashMap實例,該實例將存儲我們的BigImage對象。我們將BigImage對像作為值,並將imageName對象引用作為鍵。 imageName將作為WeakReference類型存儲在地圖中。

接下來,我們將imageName引用設置為null ,因此不再有指向bigImage對象的引用。 WeakHashMap的默認行為是在下一個GC上回收沒有引用的條目,因此下一個GC進程將從內存中刪除該條目。

我們正在調用System.gc()來強制JVM觸發GC進程。 GC週期結束後,我們的WeakHashMap將為空:

WeakHashMap<UniqueImageName, BigImage> map = new WeakHashMap<>();

 BigImage bigImageFirst = new BigImage("foo");

 UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image");



 BigImage bigImageSecond = new BigImage("foo_2");

 UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2");



 map.put(imageNameFirst, bigImageFirst);

 map.put(imageNameSecond, bigImageSecond);



 assertTrue(map.containsKey(imageNameFirst));

 assertTrue(map.containsKey(imageNameSecond));



 imageNameFirst = null;

 System.gc();



 await().atMost(10, TimeUnit.SECONDS)

 .until(() -> map.size() == 1);

 await().atMost(10, TimeUnit.SECONDS)

 .until(() -> map.containsKey(imageNameSecond));

請注意,只有imageNameFirst引用設置為nullimageNameSecond引用保持不變。觸發GC後,映射將僅包含一個條目– imageNameSecond

4。結論

在本文中,我們研究了Java中的引用類型,以充分理解java.util的方式。 WeakHashMap有效。我們創建了一個簡單的緩存,該緩存利用WeakHashMap的行為並測試它是否按預期工作。

所有這些示例和代碼段的實現都可以在GitHub項目(這是一個Maven項目)中找到,因此應該很容易直接導入和運行。