Junior 8 min · March 06, 2026

WeakHashMap in Java — Stale Keys and OutOfMemoryError

3 a.m.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Production
production tested
May 23, 2026
last updated
1,554
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • WeakHashMap holds keys via WeakReference, enabling automatic eviction when no strong reference exists.
  • Core components: WeakReference, ReferenceQueue, and expungeStaleEntries() cleanup.
  • Performance: get/put O(1) amortized, but cleanup adds overhead after GC cycles.
  • Production insight: Stale entries are removed lazily — memory isn't freed until a map operation triggers cleanup.
  • Biggest mistake: Using WeakHashMap as a cache for frequently accessed keys — it evicts based on key liveness, not memory pressure.
✦ Definition~90s read
What is WeakHashMap in Java?

WeakHashMap is a specialized Map implementation where entries are automatically removed when their keys are no longer strongly reachable from any thread. It solves the problem of memory leaks in caches or registries where you want to associate metadata with objects without preventing those objects from being garbage collected.

Imagine you're a librarian who writes down which books people are currently reading.

Unlike HashMap, which holds strong references to keys and can cause OutOfMemoryError if you keep adding entries without removal, WeakHashMap uses WeakReference wrappers around keys, allowing the GC to reclaim them when the only remaining references are weak. This makes it ideal for scenarios like per-object listeners, canonical mappings, or temporary metadata that should vanish when the key object dies.

However, WeakHashMap has sharp edges that catch even experienced developers. The eviction is non-deterministic — entries disappear only after the GC runs, which may not happen for hours under low memory pressure, or may happen mid-iteration. This means you cannot rely on WeakHashMap for time-sensitive caches or for managing resources like file handles or database connections, where prompt cleanup is critical.

It also breaks in subtle ways: if you use String literals or interned values as keys, they never become weakly reachable because the JVM holds strong references to them, effectively turning your WeakHashMap into a memory leak. For most caching needs, Guava's Cache or Caffeine with explicit eviction policies are safer choices; WeakHashMap is best reserved for low-volume, GC-tolerant mappings where simplicity matters more than predictability.

Plain-English First

Imagine you're a librarian who writes down which books people are currently reading. The moment someone returns a book and nobody else holds a copy, you automatically erase that book from your notes — you don't need to track it anymore. WeakHashMap works exactly like that: it holds references to keys, but the moment nothing else in your program is using a key, Java's garbage collector erases it from the map automatically. You never have to manually clean up. It's a map that knows when to forget.

Memory leaks are one of the nastiest production bugs in Java. You add entries to a cache, the keys go stale, but the map keeps holding onto them — the heap bloats, GC pressure climbs, and eventually you're staring at an OutOfMemoryError at 3 a.m. Most developers reach for HashMap by default without realising there's a data structure built specifically to prevent this class of problem. WeakHashMap is that structure, and understanding it deeply separates developers who react to memory leaks from those who design systems that never have them in the first place.

WeakHashMap solves the problem of strong-reference retention. In a regular HashMap, a key stored in the map is strongly reachable, which means the GC will never collect it as long as the map itself is alive. This is perfect for most cases, but catastrophic for caches where the map acts as the only reason a key object survives. WeakHashMap flips this contract: keys are held with weak references, so the GC is free to collect a key the moment no other strong reference to it exists anywhere else in your program. When a key is collected, its entry is silently removed from the map.

By the end of this article you'll understand exactly how WeakHashMap is implemented under the hood using WeakReference and ReferenceQueue, when it's the right tool and when it'll betray you, how it behaves under GC pressure with concrete runnable examples, and the three production gotchas that trip up even experienced engineers. You'll also walk away ready to answer the WeakHashMap questions that show up in senior Java interviews.

Why WeakHashMap Exists — and Where It Breaks

WeakHashMap is a Map implementation where keys are held by weak references. When a key becomes only weakly reachable — meaning no strong references exist outside the map — the garbage collector can reclaim it. The entry is then automatically removed from the map on the next operation. This is not a caching layer; it's a lifecycle management tool.

The core mechanic: WeakHashMap uses ReferenceQueue to track reclaimed keys. After a GC cycle, stale entries are expunged during subsequent map operations (get, put, size, etc.). This means map size can shrink without explicit removal. But note: values are held by strong references. If a value strongly references its own key, the key never becomes weakly reachable — a classic self-referential leak.

Use WeakHashMap when you need to associate metadata with objects whose lifetimes you don't control — for example, per-thread diagnostics or component-scoped caches in a DI container. It prevents the map from preventing GC of its keys. But it is not a general-purpose cache: entries vanish silently, and the map is not thread-safe.

Value References Can Leak
If a value holds a strong reference to its own key, the key is never reclaimed — the entire entry becomes immortal despite WeakHashMap's design.
Production Insight
A Java web framework used WeakHashMap to store per-request metrics. Under high concurrency, requests that threw exceptions never cleared their entries. The values held references to the request context, which held the key. Result: OutOfMemoryError after 2 hours of peak traffic.
Symptom: heap dump showed thousands of WeakHashMap entries with live keys, all referencing each other in a cycle.
Rule: Never let a value in a WeakHashMap reference its own key — directly or indirectly. Use WeakReference in the value if needed.
Key Takeaway
WeakHashMap keys are reclaimed by GC, not by explicit removal — entries vanish silently after the last strong reference dies.
Values are strong references — they can keep keys alive if they reference them, defeating the entire purpose.
WeakHashMap is not thread-safe and not a cache — use it only for metadata tied to object lifetimes you don't control.
WeakHashMap: Stale Keys & Memory Leaks THECODEFORGE.IO WeakHashMap: Stale Keys & Memory Leaks How weak references, GC, and stale keys cause OutOfMemoryError WeakHashMap Entry Key held by WeakReference GC Clears Weak Key Key becomes null, entry stale Stale Entry Accumulates Not removed until next get/put OutOfMemoryError Risk If stale entries dominate heap Manual Cleanup Required Call size() or iterate periodically ⚠ WeakHashMap is not a cache — entries linger after key GC Use WeakHashMap only for canonical mappings, not caching THECODEFORGE.IO
thecodeforge.io
WeakHashMap: Stale Keys & Memory Leaks
Weakhashmap Java

How WeakHashMap Works Internally

WeakHashMap wraps keys in java.lang.ref.WeakReference. When the key object loses all strong references, the GC clears the weak reference and enqueues it into an internal ReferenceQueue. On every subsequent map operation (get, put, size, etc.), the map polls the queue and removes the corresponding entries. This is the expungeStaleEntries() method — it runs lazily, not in a background thread.

Let's walk through a runnable example that demonstrates eviction:

io/thecodeforge/WeakHashMapDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package io.thecodeforge;

import java.util.WeakHashMap;

public class WeakHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        WeakHashMap<Object, String> map = new WeakHashMap<>();
        Object key = new Object(); // strong reference
        map.put(key, "value");
        System.out.println("Map size before null: " + map.size()); // 1
        key = null; // remove strong reference
        System.gc(); // hint GC
        Thread.sleep(100); // allow GC to process
        System.out.println("Map size after GC: " + map.size()); // likely 0
    }
}
Output
Map size before null: 1
Map size after GC: 0
Production Insight
Calling System.gc() is only a hint. In production, you don't control GC timing.
Map operations are required to trigger cleanup — if your map is read-only for long periods, stale entries pile up.
Always call size() or containsKey() periodically if latency-sensitive operations follow.
Key Takeaway
WeakHashMap evicts lazily, not eagerly.
Cleanup runs on next map operation, not at GC time.
If you need timely eviction, force a map touch after nullifying keys.

When to Use (and Not Use) WeakHashMap

WeakHashMap is the right choice when the map's keys are objects with well-defined lifespans tied to application logic — for example, a per-request cache where keys are request-scoped objects. It's a poor fit for caches that need time-based or memory-based eviction, or when keys are value objects like Strings that are often interned or reused.

Production Insight
Assuming WeakHashMap solves all caching problems is the #1 mistake.
In one incident, a team used WeakHashMap for a session-store with String keys — the JVM interned strings, so keys were never collected, causing a slow memory leak.
Rule: audit key reachability before committing to WeakHashMap.
Key Takeaway
WeakHashMap evicts based on key reachability, not expiration.
If your key can be strongly referenced elsewhere, eviction fails.
Match the eviction strategy to your key lifecycle, not the map's name.
WeakHashMap Decision Guide
IfKeys are objects you control and nullify after use
UseWeakHashMap works well — automatic eviction reduces memory leaks.
IfKeys are String literals or interned strings
UseAvoid — strings are often strongly referenced elsewhere, preventing eviction.
IfYou need time-based or max-size eviction
UseUse Caffeine or Guava cache — WeakHashMap has no TTL or capacity limit.
IfThe map is accessed infrequently (e.g., hours between operations)
UseConsider manual cleanup or a background thread to force eviction.

Comparing WeakHashMap with Other Map Types

Java offers several map implementations with different reference semantics. The key difference is how each handles key reachability: - HashMap: strong references to keys and values - WeakHashMap: weak references to keys (strong to values) - IdentityHashMap: uses reference equality (==) instead of equals() - EnumMap: for enum keys, high performance

For caching, WeakHashMap is often compared with SoftReference-based maps (not in JDK standard library). SoftReference allows collection only when memory is low, making it better for memory-sensitive caches.

Production Insight
Don't expect WeakHashMap to behave like a cache library — it has no size limits, TTL, or eviction listeners.
In high-throughput systems, the implicit cleanup cost (walking the ReferenceQueue) can accumulate under GC pressure.
Measure the cleanup overhead if your map is large and frequently accessed.
Key Takeaway
WeakHashMap is not a cache — it's a memory-safety tool.
For production caches, use Caffeine or Guava.
For per-object lifecycle, WeakHashMap excels.

Strong, Soft, and Weak References — The GC Trinity You Ignore at Your Peril

WeakHashMap doesn't make sense until you understand Java's reference hierarchy. Most devs only know strong references — the default. That's the problem. A strong reference is an iron chain: as long as it exists, GC can't touch the object. Integer prime = 1; — that's a strong reference. The object stays in memory until you explicitly null it or it goes out of scope.

Soft references are a pressure valve. JVM only collects them when it's about to run out of heap. They're useful for memory-sensitive caches that can survive GC cycles as long as there's breathing room. SoftReference<ImageCache> cacheRef = new SoftReference<>(new ImageCache());

Weak references are the real star here. GC will collect a weakly-reachable object on the very next cycle — no mercy, no hesitation. That's exactly what WeakHashMap uses for its keys. When your application code loses all strong references to the key object, GC evicts it immediately, and the map entry disappears. No manual cleanup. No memory leak. This is the mechanism that makes WeakHashMap's automatic eviction work, and understanding it is the difference between using this tool and getting burned by it.

ReferenceTypesDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// io.thecodeforge — java tutorial

import java.lang.ref.*;

public class ReferenceTypesDemo {
    public static void main(String[] args) {
        Object strong = new Object();         // strong reference
        SoftReference<Object> soft = new SoftReference<>(new Object());  // soft
        WeakReference<Object> weak = new WeakReference<>(new Object());  // weak

        System.gc();
        System.out.println("Strong: " + strong);
        System.out.println("Soft: " + soft.get());
        System.out.println("Weak: " + weak.get());
    }
}
Output
Strong: java.lang.Object@15db9742
Soft: java.lang.Object@6d06d69c
Weak: null
Production Trap:
Don't assume System.gc() runs immediately. This demo shows the behavior conceptually — in production, GC timing is non-deterministic. WeakHashMap relies on the GC eventually clearing, not instantly.
Key Takeaway
WeakHashMap keys are weak references — if your app holds no strong reference to the key, GC reclaims it next cycle.

WeakHashMap as an Efficient Memory Cache — What Nobody Tells You About Eviction Timing

The most common use case for WeakHashMap is a cache that should not prevent garbage collection. Think metadata: class loaders, thread-local configs, or session-scoped data that should disappear when the parent object goes away. The classic example is a class loader: you cache per-class metadata, and when the class loader is garbage collected, the cache entries vanish automatically.

But here's the catch — WeakHashMap is not a time-based or size-based cache. Eviction happens when GC runs and the key is weakly reachable. You have zero control over when that happens. It could be 5 milliseconds or 5 minutes. This makes WeakHashMap unsuitable for caches where you need predictable eviction (like LRU). Use Caffeine or Guava for that.

Another trap: if you use String literals as keys, they are interned and have a strong reference from the string pool. Your entries will never be evicted. The same applies to Integer objects within the cached range (-128 to 127). Always use custom objects or new String("value") if you want weak key semantics. Test your eviction assumptions under a real GC profile before deploying.

WeakHashMapCache.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// io.thecodeforge — java tutorial

import java.util.WeakHashMap;

public class WeakHashMapCache {
    private WeakHashMap<Object, String> cache = new WeakHashMap<>();

    public void cacheSession(Object sessionKey, String data) {
        cache.put(sessionKey, data);
    }

    public String getCachedData(Object sessionKey) {
        return cache.get(sessionKey);
    }

    public static void main(String[] args) {
        WeakHashMapCache cache = new WeakHashMapCache();
        Object session = new Object();
        cache.cacheSession(session, "expensive-computation-result");
        System.out.println("Before GC: " + cache.getCachedData(session));
        session = null;
        System.gc();
        System.out.println("After GC: " + cache.getCachedData(new Object()));
    }
}
Output
Before GC: expensive-computation-result
After GC: null
Senior Shortcut:
Use WeakHashMap only when entry lifespan is tied to key reachability, not time. For LRU, size, or time-based eviction, use Caffeine. They are not interchangeable.
Key Takeaway
WeakHashMap evicts on GC cycles, not on schedule — use it for reachability-dependent caches, not general-purpose caching.

Constructors: The Five Ways You'll Instantiate WeakHashMap

You don't just declare a WeakHashMap and move on. The constructor you pick dictates memory behavior, resizing frequency, and cache eviction semantics. Get it wrong, and you'll either waste heap or starve your map.

The default constructor gives you initial capacity 16 and load factor 0.75. Fine for prototypes. In production, every resize triggers a full GC run on the reference queue — so if you know you'll hold 10,000 entries, pass new WeakHashMap<>(10000) to avoid cascading rebuilds. The load factor constructor is rarely your friend; cranking it above 0.75 makes the map denser, which increases collision chains and slows key lookup. The map copy constructor is a trap: it clones a snapshot, but entries in the source map can be GC'd immediately after — your new map won't re-check references until the next expungeStaleEntries() call. Never use it for long-lived copies.

WeakHashMapConstructors.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// io.thecodeforge — java tutorial

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

public class WeakHashMapConstructors {
    public static void main(String[] args) {
        // Default: capacity 16, load factor 0.75
        WeakHashMap<String, String> defaultMap = new WeakHashMap<>();

        // Production: pre-size to avoid resizes
        WeakHashMap<String, String> sized = new WeakHashMap<>(10_000);

        // Aggressive load factor: 0.9 to reduce entries
        WeakHashMap<String, String> dense = new WeakHashMap<>(16, 0.9f);

        // Copy constructor: snapshot, references alive only in source
        Map<String, String> source = new HashMap<>();
        source.put("temp", "value");
        WeakHashMap<String, String> copy = new WeakHashMap<>(source);
        source.clear(); // copy now holds "temp" until next cleanup

        System.out.println("Sized map ready for 10k keys: " + sized.size());
        System.out.println("Dense map initial size: " + dense.size());
        System.out.println("Copy constructor size: " + copy.size());
    }
}
Output
Sized map ready for 10k keys: 0
Dense map initial size: 0
Copy constructor size: 1
Production Trap:
The copy constructor does NOT deep-copy reference strengths. Entries from the source become weak references in the new map; if the source goes out of scope, those keys are eligible for GC.
Key Takeaway
Always pre-size WeakHashMap to your expected load. Never rely on the copy constructor for long-lived caches.

Why WeakHashMap Is Not a Cache (and What to Use Instead)

Every new developer assumes WeakHashMap is a free memory cache. It's not. It's a map with keys that die when the JVM says so — not when you want them to. GC runs are unpredictable, so eviction timing is nondeterministic. If you need a cache with TTLs, LRU eviction, or max-size bounds, WeakHashMap will betray you.

WeakHashMap shines for ephemeral metadata: per-thread request context, temporary listeners, or object-scoped properties. The keys are short-lived objects that you expect to die naturally. For caches, use Guava's Cache or Caffeine. They offer explicit eviction policies, expiration after write, and size-based limits. WeakHashMap's "eviction" is when the GC thread decides to run—not when your application needs memory. The only thing worse than a memory leak is a cache that evicts at random.

If you must use WeakHashMap for caching, pair it with a reference queue monitor and log eviction events in development to understand the GC pattern. In production, profile heap usage before and after. You'll likely find that the "free" cache costs you more in tuning than it saves in memory.

WeakHashMapCacheTrap.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// io.thecodeforge — java tutorial

import java.util.WeakHashMap;

public class WeakHashMapCacheTrap {
    public static void main(String[] args) {
        WeakHashMap<Object, String> cache = new WeakHashMap<>();
        Object key = new Object();
        cache.put(key, "expensive-result");

        System.out.println("Before GC: " + cache.size()); // 1
        key = null;
        System.gc(); // request GC — unreliable, non-deterministic

        // Thread sleep to give GC a chance (no guarantee)
        try { Thread.sleep(100); } catch (InterruptedException e) {}

        System.out.println("After key=null and GC: " + cache.size()); // likely 0
    }
}
Output
Before GC: 1
After key=null and GC: 0
Senior Shortcut:
If you need a cache, reach for Caffeine or Guava. WeakHashMap is for one thing only: linking object lifetimes to key reachability.
Key Takeaway
WeakHashMap is not a cache manager. It's a reference manager. Use it for ephemeral metadata, not performance-critical caches.

Overview

WeakHashMap in Java is a Map implementation that stores keys using weak references. This means that when a key is no longer referenced from anywhere else in the application, the garbage collector (GC) can reclaim both the key and its associated entry from the map automatically. Unlike HashMap, WeakHashMap does not prevent its keys from being garbage collected, making it ideal for scenarios where you want to associate metadata with objects without preventing their cleanup. Built on top of the ReferenceQueue mechanism, WeakHashMap checks for collected keys during every get(), put(), or size() operation and removes stale entries lazily. This design solves the problem of memory leaks in caches and listener registries, but introduces unpredictable eviction timing since GC runs nondeterministically. Understanding when and how WeakHashMap works — and when it doesn't — is critical for building memory-safe applications.

OverviewExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — java tutorial
import java.util.WeakHashMap;

public class OverviewExample {
    public static void main(String[] args) {
        WeakHashMap<Object, String> map = new WeakHashMap<>();
        Object key = new Object();
        map.put(key, "value");
        System.out.println("Size before GC: " + map.size());
        key = null;
        System.gc();
        System.out.println("Size after GC: " + map.size());
    }
}
Output
Size before GC: 1
Size after GC: 0
Production Trap:
Calling System.gc() is just a suggestion — GC may not run immediately, so WeakHashMap eviction is nondeterministic and should never be relied upon for timing-sensitive logic.
Key Takeaway
WeakHashMap automatically removes entries when their key becomes weakly reachable, preventing memory leaks but with unpredictable eviction timing.

Conclusion

WeakHashMap is a powerful but often misunderstood tool in Java's collection framework. Its true strength lies in eliminating accidental memory leaks when keys have a limited lifecycle tied to external references — such as in caches for classloaders, temporary metadata maps, or listener registries. However, it is not a general-purpose cache alternative because eviction is tied to GC behavior, not time or memory pressure. Developers must weigh the tradeoff between automatic cleanup and nondeterministic eviction. For high-performance or predictable caching, consider using Guava's Cache or Caffeine instead. The key takeaway: use WeakHashMap only when you need automatic entry removal based on key reachability, and never rely on it for critical memory management. Proper use of constructors — with custom initial capacity, load factor, or populating from an existing Map — allows fine-tuning performance. Understanding the GC trinity of strong, soft, and weak references is non-negotiable before adopting WeakHashMap in production.

ConclusionExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// io.thecodeforge — java tutorial
import java.util.WeakHashMap;
import java.util.HashMap;
import java.util.Map;

public class ConclusionExample {
    public static void main(String[] args) {
        Map<String, Integer> source = new HashMap<>();
        source.put("a", 1);
        source.put("b", 2);
        WeakHashMap<String, Integer> map = new WeakHashMap<>(source);
        System.out.println("Populated from existing map:");
        map.forEach((k, v) -> System.out.println(k + "=" + v));
    }
}
Output
Populated from existing map:
a=1
b=2
Key Insight:
WeakHashMap's constructors accept initial capacity and load factor for tuning, but the real value comes from its automatic cleanup behavior — not from guessing memory thresholds.
Key Takeaway
WeakHashMap is best suited for reachability-driven cleanup, not as a general cache; use it only where automatic entry removal via weak keys is the actual requirement.
● Production incidentPOST-MORTEMseverity: high

Cache-Driven OutOfMemoryError in a Session Store

Symptom
Heap grew steadily over hours, culminating in OutOfMemoryError around 3 a.m. during low traffic.
Assumption
WeakHashMap would automatically evict expired sessions since the session key was only used inside request handlers.
Root cause
The session key was also referenced by a background thread that held a strong reference, making the key strongly reachable and preventing GC.
Fix
Replaced WeakHashMap with a scheduled cleanup using a ConcurrentHashMap and a TTL-based eviction policy.
Key lesson
  • WeakHashMap only evicts when keys have zero strong references — not when they 'expire'.
  • Always audit all strong references to keys before relying on WeakHashMap for eviction.
  • If you need time-based eviction, use a proper cache like Caffeine or Ehcache.
Production debug guideDiagnose stale entries, unexpected retention, and memory leaks3 entries
Symptom · 01
Heap grows even though keys seem unused
Fix
Check for hidden strong references: use heap dump analysis (jmap -dump:live,format=b,file=heap.hprof <pid>) and search for weak references to key class.
Symptom · 02
Expected eviction doesn't happen
Fix
Verify the ReferenceQueue is processed: WeakHashMap relies on queue.poll() in operations — if no map operations occur after GC, stale entries linger. Force a map.size() or get() to trigger cleanup.
Symptom · 03
Map size doesn't decrease after nullifying keys
Fix
Confirm nullification is for the exact key object used in the map. The key must be unreachable, not just set to null in a different variable.
★ WeakHashMap Debug Cheat SheetQuick commands to diagnose WeakHashMap issues in production JVMs.
Map not evicting entries
Immediate action
Trigger a map operation: call size() or containsKey()
Commands
jcmd <pid> GC.run
jmap -histo:live <pid> | grep YourKeyClass
Fix now
Add a background thread that calls map.size() every minute to force cleanup.
Suspected strong reference leak+
Immediate action
Take a heap dump and inspect references to the key class.
Commands
jmap -dump:live,format=b,file=heap.hprof <pid>
jhat heap.hprof (or Eclipse MAT)
Fix now
Use a profiler to find GC roots holding references to keys.
Map size reported incorrectly+
Immediate action
Check if size() is called frequently — it may include stale entries before cleanup.
Commands
jstack <pid> | grep expungeStaleEntries
Grep logs for 'stale' or 'weak'
Fix now
Use a custom monitoring metric that counts actual strong references.
ConceptUse CaseExample
WeakHashMap in JavaCore usageSee code above

Key takeaways

1
WeakHashMap evicts entries when keys are no longer strongly reachable
not on a timer or memory threshold.
2
Cleanup happens lazily on next map operation
call size() after nullifying keys to trigger it.
3
Not a drop-in cache replacement
use Caffeine or Guava for time/memory-based eviction.
4
Audit all strong references to key objects before relying on WeakHashMap to prevent leaks.
5
The entrySet() and iteration views are weakly consistent
expect surprises if GC runs during iteration.

Common mistakes to avoid

3 patterns
×

WeakHashMap clears stale keys automatically — no extra care needed

Symptom
Memory still grows because keys are strongly referenced elsewhere, e.g., by a background thread or string pool.
Fix
Trace all strong references to the key objects; verify that no other part of the system holds them. Use heap dump analysis.
×

Using WeakHashMap for all caching scenarios

Symptom
Frequent evictions causing performance issues, or unexpected retainment due to key reachability.
Fix
Choose a cache based on requirements: time-based eviction → Caffeine; memory-bound → SoftReference; lifecycle-bound → WeakHashMap.
×

Assuming map operations are always O(1) regardless of staleness

Symptom
Intermittent latency spikes after GC pauses due to cleanup of many stale entries.
Fix
Proactively expunge: call map.size() periodically in a background thread to spread cleanup cost.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain how WeakHashMap prevents memory leaks and what its limitations a...
Q02JUNIOR
Why doesn't WeakHashMap implement the Map interface's entrySet() correct...
Q01 of 02SENIOR

Explain how WeakHashMap prevents memory leaks and what its limitations are.

ANSWER
WeakHashMap wraps keys in WeakReference. When a key has no strong references, GC clears the WeakReference, and the entry is removed on subsequent map operations. This prevents the map from being the sole reason a key survives in memory. Limitations: - Eviction is lazy — stale entries persist until a map operation. - Values are strong-referenced — if a value refers to the key indirectly, that can create a strong reference cycle preventing eviction. - Not a general cache — lacks TTL, size limits, and eviction callbacks.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
What is WeakHashMap in Java in simple terms?
02
Does WeakHashMap guarantee that entries are removed immediately after the key is nullified?
03
Can WeakHashMap be used for a cache with time-based expiration?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Verified
production tested
May 23, 2026
last updated
1,554
articles · all by Naren
🔥

That's Collections. Mark it forged?

8 min read · try the examples if you haven't

Previous
EnumMap and EnumSet in Java
16 / 21 · Collections
Next
IdentityHashMap in Java