Що таке 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]]