How to detect and prevent memory leaks in Node.js?
Memory Leaks in Node.js
A memory leak occurs when the application allocates memory but fails to release it when it's no longer needed. Over time, memory consumption grows until the process crashes with an out-of-memory error.
Common Causes of Memory Leaks
1. Global Variables and Caches Without Limits
js
// โ Grows indefinitely
const cache = {};
app.get('/data/:id', (req, res) => {
cache[req.params.id] = fetchExpensiveData(req.params.id);
res.json(cache[req.params.id]);
});Fix: Use LRU cache with a max size:
js
// โ
Bounded cache
const LRU = require('lru-cache');
const cache = new LRU({ max: 500, ttl: 1000 * 60 * 5 });2. Unremoved Event Listeners
js
// โ New listener added on every request
app.get('/stream', (req, res) => {
process.on('data', handler); // Never removed!
});Fix: Always remove listeners:
js
// โ
Remove on disconnect
req.on('close', () => {
process.removeListener('data', handler);
});3. Closures Retaining Large Objects
js
function processData() {
const hugeArray = new Array(1e6).fill('data');
// โ Closure retains hugeArray even though only length is needed
return function getLength() {
return hugeArray.length;
};
}Fix: Extract only what's needed:
js
function processData() {
const hugeArray = new Array(1e6).fill('data');
const length = hugeArray.length;
// hugeArray can now be garbage collected
return function getLength() {
return length;
};
}4. Forgotten Timers and Intervals
js
// โ Interval never cleared
setInterval(() => {
doSomething();
}, 1000);Fix: Store and clear references:
js
const interval = setInterval(() => doSomething(), 1000);
// When done:
clearInterval(interval);5. Unreferenced Streams
Not consuming readable streams causes buffered data to pile up in memory.
Detecting Memory Leaks
Using process.memoryUsage()
js
setInterval(() => {
const mem = process.memoryUsage();
console.log({
rss: `${Math.round(mem.rss / 1024 / 1024)} MB`,
heapUsed: `${Math.round(mem.heapUsed / 1024 / 1024)} MB`,
heapTotal: `${Math.round(mem.heapTotal / 1024 / 1024)} MB`,
external: `${Math.round(mem.external / 1024 / 1024)} MB`
});
}, 5000);Using --inspect with Chrome DevTools
bash
node --inspect server.jsOpen chrome://inspect โ Take heap snapshots โ Compare them over time.
Using clinic.js
bash
npx clinic doctor -- node server.js
npx clinic heapprofiler -- node server.jsUsing --max-old-space-size
bash
# Limit heap to 512MB to catch leaks faster in development
node --max-old-space-size=512 server.jsPrevention Checklist
| Practice | Description |
|---|---|
| Use bounded caches | LRU with max size and TTL |
| Remove event listeners | Always .removeListener() or .off() |
| Avoid global state | Scope data to request lifecycle |
| Clear timers | clearInterval, clearTimeout |
| Consume streams | Always pipe or read streams |
| Monitor memory | Track process.memoryUsage() in production |
| Use WeakMap/WeakRef | For object caches that should not prevent GC |
Tip: In production, use monitoring tools like Prometheus + Grafana, Datadog, or New Relic to track memory trends and set alerts before OOM crashes.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.