Руководство по WeakHashMap в Java

Руководство по WeakHashMap в Java

1. обзор

В этой статье мы рассмотримWeakHashMap из пакетаjava.util.

Чтобы понять структуру данных, мы будем использовать ее здесь, чтобы развернуть простую реализацию кеширования. Однако имейте в виду, что это предназначено для понимания того, как работает карта, и создание собственной реализации кэша почти всегда является плохой идеей.

Проще говоря,WeakHashMap - это реализация интерфейсаMap на основе хэш-таблицы с ключами типаWeakReference.

Запись вWeakHashMap будет автоматически удалена, когда ее ключ больше не используется в обычном режиме, а это означает, что нет ни одногоReference, указывающего на этот ключ. Когда процесс сборки мусора (GC) отбрасывает ключ, его запись эффективно удаляется с карты, поэтому этот класс ведет себя несколько иначе, чем другие реализацииMap.

2. Сильные, мягкие и слабые ссылки

Чтобы понять, как работаетWeakHashMap, используйтеwe need to look at a WeakReference class - это базовая конструкция для ключей в реализацииWeakHashMap. В Java у нас есть три основных типа ссылок, которые мы объясним в следующих разделах.

2.1. Сильные Ссылки

Сильная ссылка - это наиболее распространенный типReference, который мы используем в повседневном программировании:

Integer prime = 1;

Переменнаяprime имеетstrong reference для объектаInteger со значением 1. Любой объект, на который есть сильная ссылка, не имеет права на сборщик мусора.

2.2. Мягкие ссылки

Проще говоря, объект, на который указываетSoftReference, не будет обрабатываться сборщиком мусора, пока JVM полностью не понадобится память.

Давайте посмотрим, как мы можем создатьSoftReference в Java:

Integer prime = 1;
SoftReference soft = new SoftReference(prime);
prime = null;

Объектprime имеет сильную ссылку, указывающую на него.

Затем мы превращаем сильную ссылкуprime в мягкую. После создания этой сильной ссылкиnull объектprime имеет право на сборщик мусора, но будет собран только тогда, когда JVM абсолютно потребуется память.

2.3. Слабые ссылки

Объекты, на которые ссылаются только слабые ссылки, быстро собираются мусором; В этом случае сборщик мусора не будет ждать, пока ему понадобится память.

Мы можем создатьWeakReference в Java следующим образом:

Integer prime = 1;
WeakReference soft = new WeakReference(prime);
prime = null;

Когда мы создали ссылкуprimenull, объектprime будет удален сборщиком мусора в следующем цикле сборки мусора, поскольку на него нет другой сильной ссылки.

Ссылки типаWeakReference используются как ключи вWeakHashMap.

3. WeakHashMap как эффективный кэш памяти

Допустим, мы хотим создать кеш, в котором большие объекты изображений будут храниться в качестве значений, а имена изображений - в качестве ключей. Мы хотим выбрать правильную реализацию карты для решения этой проблемы.

Использование простогоHashMap не будет хорошим выбором, поскольку объекты значений могут занимать много памяти. Более того, они никогда не будут извлечены из кеша процессом сборки мусора, даже если они больше не используются в нашем приложении.

В идеале нам нужна реализацияMap, которая позволяет GC автоматически удалять неиспользуемые объекты. Когда ключ большого объекта изображения не используется в нашем приложении в любом месте, эта запись будет удалена из памяти.

К счастью,WeakHashMap обладает именно этими характеристиками. Давайте протестируем нашWeakHashMap и посмотрим, как он себя ведет:

WeakHashMap 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 нашWeakHashMap будет пустым:

WeakHashMap 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 project, который является проектом Maven, поэтому его должно быть легко импортировать и запускать как есть.