advanced
Memory leaks
Find retained references from caches, listeners, timers, closures, global maps, request state, and unbounded queues.
A Node "memory leak" is usually unbounded retention — objects stay reachable via references you forgot to release. RSS grows until OOM kill.
Common retainers:
- Global Maps caching per user/request without TTL
- Event listeners never removed (`EventEmitter` on long-lived bus)
- Closures holding large request context
- Timers and intervals
- Unbounded job queues
// Leak: cache grows forever
const cache = new Map();
app.use((req, res, next) => {
cache.set(req.user.id, heavyObject(req));
next();
});
Diagnose with two heap snapshots under load — compare retained size. Fix with WeakMap where keys are ephemeral, TTL caches, `off`/`removeListener`, and clear timers on shutdown.
On interviews: explain reachability vs "memory leak" in GC languages; listener leak example; how you'd prove fix in staging.
Common pitfalls: conflating normal heap growth with leak; fixing symptoms by raising `--max-old-space-size` only.
The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.
Checklist:
- Bound every cache.
- Remove listeners on lifecycle end.
- Snapshot diff methodology.
- Monitor heap trend after deploy.