What is a memory leak?
A memory leak happens when your program keeps objects in memory that it no longer needs — and the garbage collector cannot reclaim them because something still holds a reference.
In languages with manual memory management (C/C++), a leak means you forgot to free() memory. In JavaScript, you rarely call free() — the garbage collector (GC) reclaims unreachable objects automatically. So a JS “leak” really means: you accidentally kept something reachable.
Why it matters
Section titled “Why it matters”| Context | Symptom |
|---|---|
| Browser tab | Tab slows down, fans spin, mobile browser kills the tab |
| SPA / dashboard | UI jank after hours of use without refresh |
| Node.js server | RSS climbs over days; eventual OOM crash or container restart |
| Serverless | Less common (short-lived), but warm instances can still leak |
flowchart LR
subgraph healthy [Healthy lifecycle]
A[Allocate] --> B[Use]
B --> C[Release references]
C --> D[GC reclaims]
end
subgraph leak [Leak lifecycle]
E[Allocate] --> F[Use]
F --> G[Hidden reference remains]
G --> H[GC cannot reclaim]
H --> I[Memory grows]
end
What is NOT a leak
Section titled “What is NOT a leak”- High baseline memory — a 200 MB Electron app isn’t leaking just because it’s large.
- Temporary spikes — loading a big JSON response spikes heap, then GC may collect it.
- GC hasn’t run yet — memory can look high between collection cycles.
A leak shows a sustained upward trend in retained memory while workload stays flat.
Broken pattern
Section titled “Broken pattern”const cache = [];function onMessage(msg) { cache.push(msg); // never evicted — grows forever}Fixed pattern
Section titled “Fixed pattern”const cache = new Map();const MAX = 1000;
function onMessage(msg) { cache.set(msg.id, msg); if (cache.size > MAX) { const oldest = cache.keys().next().value; cache.delete(oldest); }}