Execution Guide

Tool versions

ToolVersion usedNotes
Node.js23.11.0any ≥ 18
playwright-core1.62.1ships no browsers; resolved via lib/browser.mjs
Chrome for Testing149.0.7827.55 (arm64)detachedness snapshot field requires a modern V8
Python3.13only for python3 -m http.server in the manual lab

Browser resolution is identical to fe-01: $CHROME, then the newest Playwright cache build, then a system Chrome. node_modules is symlinked to fe-01's to avoid a second install; if that link is missing, npm install here.

Why CDP rather than in-page APIs. Two capabilities are unavailable to page script and essential here:

  • HeapProfiler.collectGarbage — deterministic GC. Without it you measure collection scheduling rather than retention. (--js-flags="--expose-gc" is the alternative and is clumsier.)
  • HeapProfiler.takeHeapSnapshot — the only way to see DOM nodes and the detachedness flag.

Quick start

cd fe-02-memory-model-and-leaks/src
npm run closures      # 1. closure context retention      (~10s)
npm run detached      # 2. detached DOM vs the JS heap    (~10s)
npm run listeners     # 3. listener leak + two fixes      (~15s)
npm run weakmap       # 4. Map vs WeakMap                 (~10s)
npm run growth        # 5. leak-detection method          (~30s)
npm run all

Manual lab

npm run serve         # http://localhost:8081

web/leaky-app.html is a small SPA-shaped application with four seeded leaks, for DevTools Memory-panel practice. See steps/05-devtools-hunt.md.

Tuning

N=500 SIZE=100000 npm run closures    # bigger contexts
ROWS=50000        npm run detached    # more detached nodes
CYCLES=1000       npm run listeners   # longer soak
N=10000           npm run weakmap
CYCLES=30 PER_CYCLE=200 npm run growth  # tighter regression, slower run

Reproducibility notes

  • Forced GC is two rounds. One round can leave objects reachable only from dying weak references. If your numbers are noisy, raise it before suspecting a real leak.
  • Every variant gets a fresh page. Sharing a page lets an earlier variant's leak contaminate later ones — this produced wrong data while building the module; see measured-results.md.
  • JS-heap numbers are stable to roughly ±0.1 MB. Anything smaller is noise. Snapshot detached-node counts are exact and are the stronger signal.
  • Snapshots are expensive (hundreds of ms to seconds). Do not put one inside a measurement loop.