Execution Guide
Tool versions
| Tool | Version used | Notes |
|---|---|---|
| Node.js | 23.11.0 | any ≥ 18 |
| playwright-core | 1.62.1 | ships no browsers; resolved via lib/browser.mjs |
| Chrome for Testing | 149.0.7827.55 (arm64) | detachedness snapshot field requires a modern V8 |
| Python | 3.13 | only 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 thedetachednessflag.
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.