JS Object Introspection Reference

Memory management in JavaScript

JavaScript allocates memory when you create objects and frees it through garbage collection: an object becomes collectible when no live code can still reach it. There is no free() — which makes accidental retention the main failure mode.

Reachability, not reference counting

Modern engines (V8, SpiderMonkey, JavaScriptCore) use tracing collectors: periodically they mark everything reachable from "roots" (globals, current stack, active handles) and sweep the rest. Two objects referencing each other, but unreachable from the roots, are still collected:

function f() {
  const a = {}, b = {};
  a.other = b; b.other = a;   // cycle
  return null;                // both are collectible
}

Classic leak patterns

const cache = new Map();
function memo(key, compute) {
  if (!cache.has(key)) cache.set(key, compute(key));
  return cache.get(key);      // entries live forever
}

WeakMap: keys without retention

A WeakMap holds its keys weakly: an entry disappears when the key object is otherwise unreachable. Ideal for attaching metadata without keeping objects alive:

const meta = new WeakMap();
function tag(obj, info) { meta.set(obj, info); }
// when obj is collected elsewhere, its meta entry goes too

WeakSet works the same for membership. Neither is iterable — by design, since their contents change under the collector.

WeakRef and FinalizationRegistry

WeakRef lets you peek at an object without holding it; use it for caches where a miss simply recomputes:

let ref = new WeakRef(bigObject);
const maybe = ref.deref();    // object or undefined if collected

FinalizationRegistry runs a callback after collection. Treat it as a diagnostics tool, not a resource-management mechanism: timing is unspecified, and the callback itself must not resurrect state.

Strong rule: business logic should never depend on when (or whether) the collector runs. Weak references optimise memory, not correctness.

Quick checklist

See also