Що таке WeakHashMap?
WeakHashMap зберігає ключі як слабкі посилання (WeakReference): 4. "Мертві" записи видаляються з таблиці
🟢 Junior Level
WeakHashMap — це спеціальна Map, яка автоматично видаляє записи, коли на ключ більше немає посилань.
Проста аналогія: Уявіть, що ви прив’язуєте записки до повітряних куль. Коли куля відлітає (ключ збирає збирач сміття), записка теж зникає.
Приклад:
WeakHashMap<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "Some data");
System.out.println(map.size()); // 1
key = null; // Більше немає посилань на ключ
System.gc(); // лише рекомендація — GC може не запуститися
System.out.println(map.size()); // 0 (якщо GC спрацював) або 1 (якщо ні)
Коли використовувати: Для кешування метаданих, прив’язаних до об’єктів, якими ви не керуєте.
🟡 Middle Level
Як це працює
WeakHashMap зберігає ключі як слабкі посилання (WeakReference):
- GC виявляє, що на ключ залишилося лише слабке посилання
- Поміщає посилання в
ReferenceQueue - При наступній операції над WeakHashMap викликається
expungeStaleEntries() - “Мертві” записи видаляються з таблиці
Коли WeakHashMap НЕ видалить записи
Ключ буде зібраний GC лише коли на нього немає strong посилань. Якщо хтоось ПОЗА WeakHashMap тримає strong reference на ключ — записи ніколи не видаляться:
Object key = new Object();
strongRef = key; // хтось ще тримає посилання
map.put(key, "value");
key = null;
System.gc();
// Ключ НЕ видалений — strongRef все ще існує!
map.size(); // 1
Справжній memory leak: коли ви забули прибрати strong reference на ключ, а WeakHashMap “чекає” GC.
Сценарії використання
- Кешування метаданих — прив’язка даних до об’єктів з чужого коду
- Lapsed Listener — зберігання слухачів без запобігання GC
- Thread-local дані — альтернатива ThreadLocal
Чого WeakHashMap НЕ робить
| Що | WeakHashMap | Справжній Cache (Caffeine) |
|---|---|---|
| Автоочищення по GC | Так | Ні |
| LRU/LFU eviction | Ні | Так |
| TTL (час життя) | Ні | Так |
| Розмір кешу | Не обмежений | Обмежений |
| Потокобезпечність | Ні | Так |
Типові помилки
- Використання рядкових літералів як ключів — вони в String Pool, ніколи не видаляються
- Очікування миттєвого очищення — очищення ліниве, лише при звертанні
- Забутий strong reference — хтось ще тримає посилання на ключ, GC не видалить
🔴 Senior Level
Internal Implementation
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
int hash;
Entry<K,V> next;
Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) {
super(key, queue); // Ключ — WeakReference з чергою
this.value = value;
this.hash = hash;
this.next = next;
}
}
ReferenceQueue Mechanism
// При кожній операції:
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null;) {
synchronized (queue) {
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
// Видаляємо Entry з ланцюжка
}
}
}
Очищення ліниве — лише при get, put, size, containsKey тощо.
Reachability і GC
Java визначає досяжність об’єктів:
- Strong reachability: звичайні посилання → не видаляються
- Soft reachability: через SoftReference → видаляються при нестачі пам’яті
- Weak reachability: через WeakReference → видаляються при наступному GC
- Phantom reachability: через PhantomReference → для post-mortem cleanup
WeakHashMap використовує Weak → об’єкт видалиться при наступному GC, навіть якщо пам’яті багато.
Edge Cases
- Integer Cache (-128 to 127): Integer-ключі з кешу ніколи не видаляються
- String Pool: Рядкові літерали — сильні посилання від JVM
- Thread-safety: WeakHashMap не потокобезпечна — потрібна зовнішня синхронізація
When NOT to Use
- Production caching: Використовуйте Caffeine/Guava Cache з LRU + TTL
- High-throughput: WeakHashMap повільніша за HashMap через ReferenceQueue polling
- Concurrent access: Потрібна
Collections.synchronizedMap(new WeakHashMap<>())
Production Monitoring
Для аналізу WeakHashMap:
- Моніторинг
size()— різке зменшення = GC activity - Heap dump показує ReferenceQueue з pending entries
- JFR: GC events correlate з WeakHashMap cleanup
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- WeakHashMap автоматично видаляє записи коли на ключ немає strong посилань
- Ключі зберігаються як WeakReference + ReferenceQueue; очищення ліниве (при get/put/size)
- GC видаляє ключ лише коли немає strong references ПОЗА WeakHashMap
- Сильне посилання на ключ = записи НЕ видаляться (справжній memory leak)
- НЕ використовувати рядкові літерали як ключі — вони в String Pool, ніколи не видаляються
- Не для production caching — використовуйте Caffeine/Guava Cache з LRU + TTL
Часті уточнюючі запитання:
- Чому System.gc() не гарантує видалення? — це лише рекомендація, JVM може проігнорувати
- Коли WeakHashMap не видалить записи? — хтось поза Map тримає strong reference на ключ
- Чи потокобезпечна WeakHashMap? — ні, потрібна Collections.synchronizedMap()
- Чим WeakReference відрізняється від SoftReference? — Weak видаляється при будь-якому GC, Soft — лише при нестачі пам’яті
Червоні прапорці (НЕ говорити):
- «WeakHashMap = повноцінний кеш» — ні, немає TTL, LRU, ліміту розміру
- «GC запуститься одразу після key = null» — ні, System.gc() лише рекомендація
- «Значення теж слабке посилання» — ні, лише ключ; значення = strong reference
Пов’язані теми:
- [[12. Чи можна використовувати mutable об’єкт як ключ в HashMap]]
- [[28. Як правильно підібрати початкову capacity для HashMap]]
- [[23. Що таке ConcurrentHashMap і чим він відрізняється від HashMap]]