Java WeakHashMap指南
1.概述
在本文中,我們將從java.util包中查看*WeakHashMap* 。
為了理解數據結構,我們將在這裡使用它來推出簡單的緩存實現。但是,請記住,這是為了了解地圖的工作原理,並且創建自己的緩存實現幾乎總是一個壞主意。
簡而言之, WeakHashMap是Map接口的基於哈希表的實現,其鍵為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引用設置為null 。 imageNameSecond引用保持不變。觸發GC後,映射將僅包含一個條目– imageNameSecond 。
4。結論
在本文中,我們研究了Java中的引用類型,以充分理解java.util的方式。 WeakHashMap有效。我們創建了一個簡單的緩存,該緩存利用WeakHashMap的行為並測試它是否按預期工作。
所有這些示例和代碼段的實現都可以在GitHub項目(這是一個Maven項目)中找到,因此應該很容易直接導入和運行。