Performancehigh

Memory Leaks in Node.js

A memory leak occurs when objects are retained in memory longer than necessary, preventing garbage collection. Common causes include global variables, closures holding references, event listener accumulation, and unbounded caches.

Memory anchor

Memory leaks = a hoarder's house. Event listeners pile up like newspapers by the door. Global Maps grow like junk in the attic with no eviction day. Closures are boxes you forgot about that still hold references to heavy furniture. The heap snapshot is your intervention photo—compare before and after to find what's accumulating.

Expected depth

Common Node.js memory leak patterns: (1) Event listeners added but never removed (EventEmitter warns at >10 listeners by default—heed these warnings). (2) Closures capturing large objects in long-lived scopes. (3) Global Maps/Sets used as caches without eviction (use Map with LRU eviction or WeakMap for object-keyed caches). (4) Request-scoped data stored in module-level variables. (5) Timer intervals (setInterval) that are never cleared. Detection: monitor process.memoryUsage().heapUsed over time; use --expose-gc with global.gc() to force GC in tests.

Deep — senior internals

V8's heap snapshots (via node --inspect + Chrome DevTools 'Memory' tab, or heapdump npm module) show object retention trees—which roots hold references to retained objects. The key workflow: take a baseline snapshot, perform the suspected leaking operation N times, take another snapshot, and diff them to identify objects that grew in count. The 'Retainers' pane shows the reference chain preventing GC. WeakRef and FinalizationRegistry (Node.js 14+) enable weak references that don't prevent GC, useful for implementing memoization caches that release under memory pressure. process.memoryUsage() returns rss (resident set size), heapTotal, heapUsed, external (V8 external allocations like Buffers), and arrayBuffers.

🎤Interview-ready answer

Node.js memory leaks typically stem from retained closures, accumulating event listeners, unbounded global caches, or setInterval never being cleared. Diagnosis uses V8 heap snapshots (via --inspect and Chrome DevTools) to diff heap between suspected leak operations and trace the retention chain. In production, monitor heapUsed trend; a consistently rising sawtooth pattern without a ceiling indicates a leak.

Common trap

Buffer.allocUnsafe(size) is tracked in process.memoryUsage().external, not heapUsed. Leaking Buffers (e.g., from streams that aren't properly closed) won't show up in heap snapshots or heapUsed metrics—you need to monitor external and arrayBuffers too. Large Buffer leaks can exhaust native heap memory without any V8 GC alarm.

Related concepts