Diagnosing Memory Leaks and Profiling Performance
Find the actual leak and the actual bottleneck inside one Node process
- attach the V8 inspector with
--inspectand capture profiles headlessly with--cpu-prof/--heapsnapshot-near-heap-limit; - read
process.memoryUsage()and tell a real leak apart from normal GC lag; - use the three-snapshot technique and the difference between shallow and retained size to find what holds memory;
- read a flame graph — self time vs total time — to find the hot path;
- measure and fix event-loop blocking with
monitorEventLoopDelay; - quantify a fix by comparing before/after metrics under load.
1. Why in-process diagnosis is a separate skill
Scaling out (the Performance and Clustering topic) multiplies whatever you already have. If each worker leaks 5 MB a minute, running eight workers just means you leak 40 MB a minute across eight processes that all restart on OOM. Adding hardware hides the symptom for a while and raises your bill; it never removes the defect. Diagnosis is the opposite move: you shrink the problem to one process, attach instruments, and look inside.
Two failure shapes dominate long-lived Node services:
- Memory leaks. Live heap grows without bound because something keeps a reference that should have been released. Resident memory (RSS) creeps up for hours or days, then the kernel OOM-kills the process (or a container hits its limit and restarts).
- Event-loop blocking. A single synchronous, CPU-heavy call freezes the one thread that serves every request. Throughput collapses and tail latency (p99) explodes even though average CPU looks modest.
Analogy. A leak is a slow drip filling a bathtub: the level rises quietly until it overflows, and buying a bigger tub (more RAM) only delays the flood. Event-loop blocking is a single checkout lane at a busy shop — it does not matter how many customers arrive, everyone waits behind the one person doing a slow task. Profiling is the diagnostic scan that tells you which pipe drips and which lane is jammed, instead of guessing.