Question 27 · Section 10

What is WeakHashMap?

WeakHashMap stores keys as weak references (WeakReference): 4. "Dead" entries are removed from the table

Language versions: English Russian Ukrainian

🟢 Junior Level

WeakHashMap is a special Map that automatically removes entries when there are no more references to the key.

Simple analogy: Imagine you tie notes to balloons. When the balloon flies away (the key is garbage collected), the note disappears too.

Example:

WeakHashMap<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "Some data");

System.out.println(map.size()); // 1

key = null; // No more references to the key
System.gc(); // only a recommendation — GC may not run

System.out.println(map.size()); // 0 (if GC ran) or 1 (if not)

When to use: For caching metadata tied to objects you don’t control.

🟡 Middle Level

How It Works

WeakHashMap stores keys as weak references (WeakReference):

  1. GC detects that only a weak reference remains for the key
  2. Puts the reference in a ReferenceQueue
  3. On the next WeakHashMap operation, expungeStaleEntries() is called
  4. “Dead” entries are removed from the table

When WeakHashMap Will NOT Remove Entries

The key will be collected by GC only when there are no strong references to it. If someone OUTSIDE the WeakHashMap holds a strong reference to the key — entries will never be removed:

Object key = new Object();
strongRef = key;  // someone else holds a reference
map.put(key, "value");
key = null;
System.gc();
// Key NOT removed — strongRef still exists!
map.size(); // 1

Real memory leak: when you forgot to remove the strong reference to the key, and WeakHashMap is “waiting” for GC.

Use Cases

  1. Metadata caching — attaching data to objects from foreign code
  2. Lapsed Listener — storing listeners without preventing GC
  3. Thread-local data — ThreadLocal alternative

What WeakHashMap Does NOT Do

Feature WeakHashMap Real Cache (Caffeine)
Auto-cleanup by GC Yes No
LRU/LFU eviction No Yes
TTL (lifetime) No Yes
Cache size Unlimited Limited
Thread safety No Yes

Common Mistakes

  1. Using string literals as keys — they’re in the String Pool, never removed
  2. Expecting instant cleanup — cleanup is lazy, only on access
  3. Forgotten strong reference — someone outside the Map holds a reference to the key, GC won’t remove it

🔴 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); // Key — WeakReference with queue
        this.value = value;
        this.hash = hash;
        this.next = next;
    }
}

ReferenceQueue Mechanism

// On every operation:
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);
            // Remove Entry from the chain
        }
    }
}

Cleanup is lazy — only on get, put, size, containsKey, etc.

Reachability and GC

Java determines object reachability:

  • Strong reachability: regular references → not removed
  • Soft reachability: via SoftReference → removed on low memory
  • Weak reachability: via WeakReference → removed on next GC
  • Phantom reachability: via PhantomReference → for post-mortem cleanup

WeakHashMap uses Weak → object is removed on the next GC, even if there’s plenty of memory.

Edge Cases

  1. Integer Cache (-128 to 127): Integer keys from the cache are never removed
  2. String Pool: String literals — strong references from the JVM
  3. Thread safety: WeakHashMap is not thread-safe — external synchronization is needed

When NOT to Use

  • Production caching: Use Caffeine/Guava Cache with LRU + TTL
  • High-throughput: WeakHashMap is slower than HashMap due to ReferenceQueue polling
  • Concurrent access: Need Collections.synchronizedMap(new WeakHashMap<>())

Production Monitoring

For analyzing WeakHashMap:

  • Monitor size() — sharp decrease = GC activity
  • Heap dump shows ReferenceQueue with pending entries
  • JFR: GC events correlate with WeakHashMap cleanup

🎯 Interview Cheat Sheet

Must know:

  • WeakHashMap automatically removes entries when there are no strong references to the key
  • Keys stored as WeakReference + ReferenceQueue; cleanup is lazy (on get/put/size)
  • GC removes the key only when there are no strong references OUTSIDE the WeakHashMap
  • Strong reference to key = entries NOT removed (real memory leak)
  • Do NOT use string literals as keys — they’re in String Pool, never removed
  • Not for production caching — use Caffeine/Guava Cache with LRU + TTL

Common follow-up questions:

  • Why doesn’t System.gc() guarantee removal? — it’s only a recommendation, JVM may ignore it
  • When won’t WeakHashMap remove entries? — someone outside the Map holds a strong reference to the key
  • Is WeakHashMap thread-safe? — no, needs Collections.synchronizedMap()
  • How does WeakReference differ from SoftReference? — Weak is removed on any GC, Soft — only on low memory

Red flags (DO NOT say):

  • “WeakHashMap = a full-featured cache” — no, no TTL, LRU, or size limit
  • “GC runs immediately after key = null” — no, System.gc() is only a recommendation
  • “The value is also a weak reference” — no, only the key; value = strong reference

Related topics:

  • [[12. Can You Use a Mutable Object as a Key in HashMap]]
  • [[28. How to Choose the Initial Capacity for HashMap]]
  • [[23. What is ConcurrentHashMap and How Does It Differ from HashMap]]