00:00

WeakHashMap in Java – Guide with Examples

WeakHashMap is a Map whose keys are held by weak references. When a key is no longer used anywhere else in your program, the garbage collector may delete it, and its entry quietly disappears from the map.

That makes WeakHashMap useful for attaching extra data to objects without stopping them from being garbage-collected, a common cause of memory leaks with ordinary maps. It lives in java.util and has been available since JDK 1.2.

1. What Is a WeakHashMap?

In a normal HashMap, the map holds a strong reference to each key. As long as the map exists, its keys can never be garbage-collected, even if nothing else uses them. A WeakHashMap holds keys weakly: the map alone does not keep a key alive.

Think of a coat check that throws away a coat once its owner has left the building. The ticket (the entry) is only useful while the owner (the key) is still around.

  • Weak keys, strong values.
  • Entries are removed automatically after their key is garbage-collected.
  • Otherwise behaves like HashMap: O(1) operations, one null key, null values, no order.
  • Not synchronized.
  • size() and iteration results can change between calls even if your code does not modify the map.

2. Class Hierarchy and Declaration

WeakHashMap class hierarchy in Java
java.util.WeakHashMap
public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>

Unlike HashMap, it does not implement Cloneable or Serializable.

3. Strong, Soft, Weak and Phantom References

Java has four strengths of reference, from java.lang.ref:

ReferenceWhen the object can be collectedTypical use
Strong (normal variable)Never, while the reference existsEveryday code
SoftReferenceOnly when memory is running lowMemory-sensitive caches
WeakReferenceAt the next GC, if no strong references remainWeakHashMap keys, listeners
PhantomReferenceAlready collected; used for clean-up notificationResource clean-up (Cleaner)

4. WeakHashMap vs HashMap in Action

Both maps receive one session object. After the program drops its own references and the garbage collector runs, only the WeakHashMap lets go:

WeakVsHashMap.java
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

public class WeakVsHashMap {
    static class Session {
        final String user;
        Session(String user) { this.user = user; }
    }

    // Ask the JVM to collect garbage until the map shrinks (or we give up)
    static void waitForGc(Map<?, ?> map, int expectedSize) throws InterruptedException {
        for (int i = 0; i < 50 && map.size() != expectedSize; i++) {
            System.gc();
            Thread.sleep(20);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Map<Session, String> strong = new HashMap<>();
        Map<Session, String> weak = new WeakHashMap<>();

        Session s1 = new Session("asha");
        Session s2 = new Session("ravi");
        strong.put(s1, "cart: 3 items");
        weak.put(s2, "cart: 1 item");
        System.out.println("Before: HashMap size=" + strong.size() + ", WeakHashMap size=" + weak.size());

        s1 = null;            // no more references from our code...
        s2 = null;
        waitForGc(weak, 0);

        System.out.println("After GC: HashMap size=" + strong.size() + ", WeakHashMap size=" + weak.size());
    }
}
Output
Before: HashMap size=1, WeakHashMap size=1
After GC: HashMap size=1, WeakHashMap size=0

System.gc() is only a request, so the example asks a few times. Real programs never need to call it; the entries disappear whenever the JVM decides to collect garbage.

5. How WeakHashMap Works Internally

  1. Each entry is a subclass of WeakReference pointing to the key, plus a strong field for the value.
  2. Every entry is registered with a ReferenceQueue.
  3. When the GC collects a key, it puts the entry's weak reference into that queue.
  4. On the next call to almost any map method (get, put, size …), the map drains the queue (expungeStaleEntries()) and removes those entries.

So entries are removed lazily, during later map operations, not at the exact moment of garbage collection.

6. Common Pitfalls

String literals and cached values as keys

String literals are stored in the string pool and stay reachable for the life of the class, so they are never collected. Small Integer values (−128 to 127) and enum constants are cached too.

LiteralKeyPitfall.java
import java.util.Map;
import java.util.WeakHashMap;

public class LiteralKeyPitfall {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> map = new WeakHashMap<>();

        String literal = "config";                 // string literals are interned: always reachable
        String fresh = new String("temp");         // a new, collectable String object
        map.put(literal, 1);
        map.put(fresh, 2);

        literal = null;
        fresh = null;
        for (int i = 0; i < 50 && map.size() > 1; i++) {
            System.gc();
            Thread.sleep(20);
        }
        System.out.println("Remaining keys: " + map.keySet());
    }
}
Output
Remaining keys: [config]

Values that refer to their own keys

Values are held strongly. If a value holds a reference to its key, for example map.put(user, new Profile(user)), the key is always reachable through the map itself and is never removed. Wrap such values in a WeakReference, or avoid the back-reference.

Equality-based keys

WeakHashMap compares keys with equals(). If a key is collected, a new but equal object can no longer find the old entry. WeakHashMap works best with keys that use identity-based equals(), such as most framework objects.

7. Practical Use: Metadata for Objects You Do Not Own

Suppose you want to attach a label to UI components created by someone else. A HashMap would keep every component alive forever; a WeakHashMap lets discarded components go:

ObjectMetadata.java
import java.util.Map;
import java.util.WeakHashMap;

public class ObjectMetadata {
    // Extra information about objects we do not own, without keeping them alive
    private static final Map<Object, String> labels = new WeakHashMap<>();

    static void label(Object o, String text) { labels.put(o, text); }
    static String labelOf(Object o) { return labels.getOrDefault(o, "(none)"); }

    public static void main(String[] args) throws InterruptedException {
        Object button = new Object();
        Object dialog = new Object();
        label(button, "OK button");
        label(dialog, "Settings dialog");

        System.out.println("button -> " + labelOf(button));
        System.out.println("tracked objects: " + labels.size());

        dialog = null;                              // the dialog is closed and discarded
        for (int i = 0; i < 50 && labels.size() > 1; i++) {
            System.gc();
            Thread.sleep(20);
        }
        System.out.println("tracked objects after GC: " + labels.size());
        System.out.println("button still labelled: " + labelOf(button));   // button is still in use
    }
}
Output
button -> OK button
tracked objects: 2
tracked objects after GC: 1
button still labelled: OK button

Other uses include listener registries, per-class or per-classloader caches, and canonicalizing maps. For general caches that should expire by size or time, a dedicated cache library is a better choice.

8. Methods

WeakHashMap has the standard Map methods and no extra public methods:

MethodNote
put, get, remove, containsKeyO(1) average; each call also clears stale entries.
size(), isEmpty()Can decrease between calls as keys are collected.
keySet(), values(), entrySet()Iterators are fail-fast; entries may vanish between iterations.
putIfAbsent, merge, compute*, forEachInherited default methods.

Constructors match HashMap: no-argument (capacity 16, load factor 0.75), with capacity, with capacity and load factor, and from another map.

9. WeakHashMap vs HashMap

FeatureWeakHashMapHashMap
Key referencesWeakStrong
Entries removed automaticallyYes, after key is collectedNever
Stable size()NoYes
Treeified bucketsNoYes (Java 8+)
Cloneable / SerializableNoYes
Main useMetadata and caches tied to object lifetimeGeneral key–value storage

10. Thread Safety

WeakHashMap is not synchronized. Wrap it with Collections.synchronizedMap(new WeakHashMap<>()) when several threads use it. There is no concurrent weak map in the JDK; caching libraries such as Caffeine offer weak keys with full concurrency.

11. Summary

  • WeakHashMap holds keys weakly, so unused keys can be garbage-collected and their entries removed.
  • Values are held strongly; values must not refer back to their keys.
  • Avoid string literals, small cached integers and enums as keys; they never become unreachable.
  • Stale entries are cleared lazily during later map operations.
  • It is useful for object metadata and listener registries, and is not thread-safe.

Related Tutorials

Reference: Java SE 21 API: java.util.WeakHashMap