Memory, Garbage Collection, and Performance
Understand reachability, garbage collection, memory leaks, object allocation, hidden performance costs, and practical optimization techniques.
Memory Is About Reachability
JavaScript engines automatically manage memory using garbage collection. A simplified but powerful mental model is reachability: an object is collectible when it is no longer reachable from roots used by the runtime.
Creating an object does not guarantee it remains forever. Conversely, an object can become a memory leak if an application unintentionally keeps a reference to it.
- Garbage collection is based on reachability rather than a simple time limit.
- Unreachable objects can eventually be reclaimed.
- A memory leak occurs when unwanted objects remain reachable.
When is an object generally eligible for garbage collection?
Common JavaScript Memory Leaks
Common leak patterns include accidentally retained global references, event listeners that remain attached unnecessarily, timers that keep closures alive, caches that grow without bounds, and long-lived data structures retaining objects that should have been released.
Closures themselves are not memory leaks. They become problematic when a long-lived object retains a closure that in turn retains a large object graph unnecessarily.
const cache = new Map();
function remember(key, value) {
cache.set(key, value);
}
// If keys continuously grow and entries are never removed,
// the cache can retain more and more memory.- Closures are not inherently memory leaks.
- Long-lived references can retain large object graphs.
- Caches should have eviction or bounded lifetime when appropriate.
Which pattern can cause a memory leak?
WeakMap and WeakSet
WeakMap and WeakSet allow garbage collection of objects when there are no other strong references to those objects. WeakMap keys must be objects or non-registered symbols. Weak collections are not enumerable because their contents can disappear as garbage collection occurs.
They are useful for metadata associated with object lifetimes, private associations, and certain caching strategies.
const metadata = new WeakMap();
let element = {};
metadata.set(element, { createdAt: Date.now() });
console.log(metadata.get(element));
element = null;
// The original object can now become collectible.- WeakMap keys do not keep their objects alive by themselves.
- Weak collections are intentionally not enumerable.
- WeakMap is useful for object-associated metadata.
Why can WeakMap help avoid retaining an object?
Performance: Measure Before Optimizing
JavaScript performance depends on many factors: algorithmic complexity, allocations, DOM work, network latency, parsing, compilation, garbage collection, and host behavior. Micro-optimizing syntax without measurement often produces complexity without meaningful gains.
Start with profiling and identify the actual bottleneck. Improve algorithms and architecture first, then optimize hot paths supported by measurements.
const users = getUsers();
const usersById = new Map(
users.map(user => [user.id, user])
);
const user = usersById.get(500);- Measure before optimizing.
- Algorithmic improvements usually matter more than syntax-level micro-optimizations.
- Use appropriate data structures for access patterns.
What should usually happen before a performance optimization?