JavaScript WeakMap and WeakSet Performance
JavaScript WeakMap and WeakSet provide
specialized, memory-efficient data structures designed for associating
metadata with objects without preventing garbage collection. This
article analyzes their runtime complexity, memory overhead, garbage
collection behavior, and engine-level performance trade-offs compared to
standard collections.
Time Complexity
The core operations for both WeakMap and
WeakSet execute in amortized constant time:
WeakMap.prototype.get(key): \(O(1)\) average lookup time.WeakMap.prototype.set(key, value): \(O(1)\) average insertion time.WeakMap.prototype.has(key)/WeakSet.prototype.has(value): \(O(1)\) average search time.WeakMap.prototype.delete(key)/WeakSet.prototype.delete(value): \(O(1)\) average removal time.
Like standard Map and Set, modern
JavaScript engines (such as V8, SpiderMonkey, and JavaScriptCore)
implement these collections using optimized hash table variants,
ensuring near-instantaneous access regardless of collection size.
Memory Overhead and Garbage Collection
The primary performance distinction between weak and strong collections lies in memory retention:
- Ephemeron References: Keys in a
WeakMapand values in aWeakSetare held weakly. If no other strong reference to a key object exists, the engine can reclaim the key, its associated value (inWeakMap), and the table entry during standard garbage collection (GC) cycles. - Prevention of Memory Leaks: In a standard
Map, stale keys remain in memory until explicitly removed via.delete()or.clear(). Weak collections eliminate this manual cleanup overhead and remove the risk of memory fragmentation caused by forgotten object references. - No Retained Property Overhead: Attaching metadata
to objects via
WeakMapdoes not mutate the original object, avoiding hidden class transitions (shapes/maps) in engines like V8, which preserves inline caching performance.
Non-Enumerable Optimization
WeakMap and WeakSet are non-iterable and do
not provide a .size property or iteration methods
(.keys(), .values(), .entries(),
forEach).
This architectural constraint yields several performance advantages:
* No Ordering Overhead: Standard Map and
Set must track insertion order to adhere to the ECMAScript
iteration specification. Weak collections do not track insertion order,
reducing internal pointer management and allocation cost. * No
Size Bookkeeping: Engines do not need to update an internal
counter dynamically upon GC-driven reclamation, keeping the runtime
footprint minimal.
Performance Comparison: Weak vs. Strong Collections
| Characteristic | Map / Set |
WeakMap /
WeakSet |
|---|---|---|
| Lookup Speed | \(O(1)\) | \(O(1)\) |
| Insertion Speed | \(O(1)\) | \(O(1)\) |
| Iteration Overhead | Yes (preserves insertion order) | None (non-iterable) |
| Object Key Holding | Strong (prevents GC) | Weak (allows GC) |
| Memory Footprint | Grows until manual deletion | Dynamically bounded by active references |
| Engine Optimization | General hash map structures | Ephemeron tables |
Engine Implementation Details
In engines like V8, WeakMap uses ephemeron pairs where
the value is reachable only if the key is reachable. During the
mark-and-sweep phase, the garbage collector performs transitive closure
analysis over ephemerons. While this can introduce negligible overhead
to specific GC tracing phases when dealing with millions of entries, it
is significantly more performant and cache-friendly than holding dead
objects in memory.