Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 18

Memory Model and Garbage Collection

Assumes you're fluent with closures, prototypes, and async execution from earlier chapters.
Level: AdvancedLength: Mid2 min read

Read at another depth

Memory Model and Garbage Collection — and Proxy and Reflect

Assumes you're fluent with closures, prototypes, and async execution from earlier chapters.

Internals

JavaScript engines allocate values on the stack (locals with known lifetimes) or the heap (objects, closures, anything escaping the frame). Most engines implement generational copying collection: the nursery (young generation) collects frequently and cheaply via Cheney semispace scavenging; survivors promote to the old generation, where an incremental mark-and-sweep with a mark bitmap, write barriers, and concurrent marking phases runs on background threads. Major collections are interleaved with allocation and can pause mutator threads in sub-millisecond slices.

Memory is reachable-ness based, not reference-count based: cycles are collected, but a value is retained as long as any live root can reach it through the heap graph. WeakRef and WeakMap hold values without pinning them.

Code example in javascript
const cache = new WeakMap();
function meta(obj) {
  if (!cache.has(obj)) cache.set(obj, { loaded: Date.now() });
  return cache.get(obj);
} // entries vanish once obj is otherwise unreachable

Trade offs

  • Copy vs. mark: scavenging is fast and cache-friendly but pays twice for long-lived objects via promotion; mark-sweep avoids copying but fragments and costs proportional to live set.
  • Incremental vs. stop-the-world: incremental marking keeps frame budgets intact but requires write barriers on every reference store.
  • WeakRef for caches: avoids leaks, but finalization timing is unspecified — never rely on it for semantics, only optimization.
  • Proxies for instrumentation: universal interception without touching call sites, at the cost of allocation, indirection, and invariant checks on every trap.

Failure modes

  • Accidental retention: long-lived closures, module-level arrays, timers, and detached DOM nodes keep whole object graphs alive. A setInterval holding this is a classic.
  • Invisible edges: WeakMap keys must be objects — primitives throw — and FinalizationRegistry callbacks are only a hint, never a guarantee.
  • Proxy invariants: a get trap returning a non-configurable, non-writable target property's actual value mismatches the invariant and throws; an ownKeys trap on a non-extensible target must report the target's exact keys.
  • Reflect.get vs. raw access: inside traps, accessing target.prop directly re-triggers the invariant machinery; route through Reflect with the correct receiver to preserve prototype lookups (notably getters bound to a subclass receiver).
Code example in javascript
const logged = new Proxy(new Map(), {
  get(target, prop, receiver) {
    if (prop === 'set') return (k, v) => { console.log('set', k); return target.set(k, v); };
    return Reflect.get(target, prop, receiver);
  },
});
logged.set('a', 1);

Trade offs (choosing whether to intercept)

  1. Use Proxy when call sites must remain untouched or per-object behavior must be dynamic (ORMs, reactivity systems like Vue's).
  2. Use plain wrapper objects when performance matters — proxies are roughly an order of magnitude slower on hot paths and break identity assumptions (Array.isArray still works, but proxy === target fails).
  3. Use Reflect alongside Proxy to forward default behavior cleanly; standalone, it replaces scattered Object/Function statics with a uniform, receiver-aware API.

diagram mermaid caption: Reachability: roots, live graph, and a weakly-held value alt: A graph showing global scope and an active closure as roots; objects reachable from roots are live; a node held only by a WeakMap is eligible for collection.

exercise Q: Why does Reflect.get(target, 'prop', receiver) matter inside a get trap, instead of returning target.prop? A: Getters on the target capture this from the receiver. Returning target.prop binds this to target, breaking subclass or proxy-context expectations; Reflect.get forwards the correct receiver and preserves prototype-chain semantics.

Code example in text

exercise Q: You replace obj with new Proxy(obj, handler) but a Set of users deduplicates them wrongly. Why? A: Because proxy !== target: identity-based collections (Set, Map, WeakMap) treat the proxy and its target as distinct values. Route all references through one canonical object.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Memory Model and Garbage Collection

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that