Что такое WeakHashMap?
4. Value → Key ссылки → утечка! 5. Вызывайте методы регулярно для очистки 6. Heap Dump → ищите Entry с null referent
🟢 Junior Level
WeakHashMap — Map, где ключи могут автоматически удаляться, когда на них больше нет ссылок.
Простая аналогия: Вы кладёте вещи на полку. Если вы забыли про вещь и никому она не нужна → уборщик её выбрасывает.
WeakHashMap<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "value");
key = null; // Больше нет ссылок на ключ!
System.gc(); // System.gc() — лишь рекомендация, в реальности удаление произойдёт при следующем GC
map.size(); // → 0 (запись удалена!)
🟡 Middle Level
Как это работает
WeakReference — ссылка, не мешающая GC удалить объект. Если на объект есть только WeakReference — GC его удалит.
ReferenceQueue — очередь, куда GC кладёт «умершие» ссылки. WeakHashMap периодически проверяет эту очередь.
expungeStaleEntries() — внутренний метод WeakHashMap, удаляющий записи с мёртвыми ключами при каждой операции (get, put, size).
1. Ключ обёрнут в WeakReference
2. При GC: если нет сильных ссылок → ключ удаляется
3. Entry попадает в ReferenceQueue
4. При следующем вызове ANY метода map:
→ expungeStaleEntries() очищает очередь
→ Запись удаляется из мапы
Ловушки
1. String literals не удаляются:
// ❌ Никогда не удалится!
WeakHashMap<String, String> map = new WeakHashMap<>();
map.put("literal", "value"); // Литерал живёт вечно в String Pool!
2. Strong reference из Value в Key:
// ❌ Утечка!
class Value { Object keyRef; }
map.put(key, new Value(key)); // Value держит ключ!
// Ключ никогда не удалится!
Когда использовать
// ✅ Метаданные для объектов
WeakHashMap<Component, String> tooltips = new WeakHashMap<>();
tooltips.put(button, "Click me");
// Когда button удалён → tooltip удалится автоматически
// ✅ НЕ для кэшей!
// → WeakReference очищается при КАЖДОМ Minor GC
// → Minor GC — сборка мусора в Young Generation, происходит каждые секунды-миллисекунды при активном создании объектов.
// → Для кэшей: Caffeine или SoftReference
// **Почему НЕ для кэшей:** WeakHashMap удаляет записи при КАЖДОМ Minor GC.
// Если вы закэшируете результат дорогого вычисления, он может исчезнуть через миллисекунды.
// Для кэшей используйте WeakHashMap только если ключи — уникальные объекты-идентификаторы, а не данные.
// Или лучше — java.lang.ref.SoftReference (не удаляется при Minor GC).
🔴 Senior Level
ReferenceQueue механизм
// WeakHashMap внутри:
class Entry<K,V> extends WeakReference<K> {
V value;
Entry<K,V> next;
Entry(K key, V value, ReferenceQueue<K> queue) {
super(key, queue); // Queue для уведомлений
this.value = value;
}
}
// GC обнаружил weak-only ключ
// → Помещает Entry в ReferenceQueue
// → expungeStaleEntries() при следующем вызове map
Lazy очистка
// WeakHashMap НЕ очищается сам по себе!
// Очистка происходит при вызове ЛЮБОГО метода:
map.put(new Object(), "x");
// ... GC удалил ключ ...
// map всё ещё содержит "мёртвую" запись!
map.size(); // → Только тут очистится!
// → Если долго не вызывать методы → утечка!
WeakHashMap vs альтернативы
| Решение | Когда |
|---|---|
| WeakHashMap | Lifecycle привязан к ключу |
| Caffeine Cache | Полноценный кэш (LRU, TTL) |
| SoftReference | Кэш, очистка при OOM угрозе |
| ThreadLocal | Данные привязаны к потоку |
Отличие от Map<WeakReference<K>, V>: WeakHashMap автоматически удаляет мёртвые записи — вам не нужно вручную чистить ReferenceQueue. В Map<WeakReference<K>, V> мёртвые ссылки копятся до бесконечности.
Диагностика утечек
Heap Dump: ищите Entry с referent = null
→ Entry всё ещё в table массиве
→ Методы мапы долго не вызывались
→ "Ленивая очистка" не сработала
Решение: map.size() принудительно очистит
Production Experience
Реальный сценарий: Component metadata
// UI фреймворк: мета-данные для компонентов
WeakHashMap<Component, Properties> meta = new WeakHashMap<>();
// Компонент удалён → meta автоматически чистится
// → Нет утечек памяти!
Best Practices
- Метаданные — основное применение
- НЕ кэш → слишком агрессивная очистка
- String literals → никогда не удалятся
- Value → Key ссылки → утечка!
- Вызывайте методы регулярно для очистки
- Heap Dump → ищите Entry с null referent
Резюме для Senior
- WeakHashMap = авто-удаление по lifecycle ключа
- ReferenceQueue → lazy очистка при вызове методов
- String literals → не удаляются (String Pool)
- Value → Key → strong reference = утечка
- НЕ кэш → используйте Caffeine
- Метаданные → основное применение
- Heap Dump → Entry с null referent
🎯 Шпаргалка для интервью
Обязательно знать:
- WeakHashMap — ключи обёрнуты в WeakReference, удаляются автоматически когда нет сильных ссылок
- ReferenceQueue — GC кладёт «умершие» ссылки, WeakHashMap проверяет очередь при вызове любого метода
- expungeStaleEntries() — lazy очистка, вызывается при get/put/size — если долго не вызывать, утечка памяти
- String literals НЕ удаляются — живут вечно в String Pool
- Strong reference из Value в Key = утечка! Key никогда не будет собран GC
- Основное применение: метаданные (tooltip, свойства UI-компонентов), НЕ кэши
- Для кэшей: Caffeine (LRU, TTL) или SoftReference (не удаляется при Minor GC)
- Отличие от Map<WeakReference
, V>: WeakHashMap автоматически чистит мёртвые записи, вручную не нужно
Частые уточняющие вопросы:
- Почему WeakHashMap НЕ подходит для кэшей? — Удаляет записи при КАЖДОМ Minor GC. Закэшированный результат дорогого вычисления может исчезнуть через миллисекунды.
- Как принудительно очистить WeakHashMap? — Вызвать любой метод: size(), get(), put(). expungeStaleEntries() проверит ReferenceQueue и удалит мёртвые записи.
- Что будет если Value ссылается на Key? — Strong reference из Value не даст Key быть собранным GC — запись никогда не удалится, утечка памяти.
- Почему String literals не удаляются из WeakHashMap? — Строковые литералы живут в String Pool — на них всегда есть сильная ссылка от ClassLoader.
Красные флаги (НЕ говорить):
- ❌ «WeakHashMap — хороший кэш» — слишком агрессивная очистка, используйте Caffeine
- ❌ «WeakHashMap очищается сам по себе в фоне» — очистка только при вызове методов (lazy)
- ❌ «String literals удаляются из WeakHashMap» — нет, они живут в String Pool вечно
- ❌ «WeakReference и SoftReference — одно и то же» — SoftReference не удаляется при Minor GC, только при OOM угрозе
Связанные темы:
- [[14. Что такое Map и какие реализации существуют]]
- [[15. В чём разница между HashMap, LinkedHashMap и TreeMap]]
- [[18. Что такое ConcurrentHashMap]]