NorthwindNorthwind
← All posts

Debugging a Memory Leak in Production Node.js

RSS climbed 40MB an hour until the pod OOM'd. A heap snapshot and one closure later, we found it.

Tom Becker · 1 min read
Share

The graph was a perfect ramp: memory up and to the right until Kubernetes killed the pod and the cycle restarted.

Catch it in the act

# Grab a heap snapshot from a running process
kill -USR2 <pid>   # with --heapsnapshot-signal=SIGUSR2

Comparing two snapshots an hour apart, one retained set kept growing: an event listener added per request and never removed. A closure captured the request object, so every request leaked its whole context.

// The leak: a listener added every request, never cleaned up
emitter.on("tick", () => process(req)); // req is captured forever

The fix was once plus an explicit off on teardown. RSS went flat.

Leaks are rarely exotic. They're almost always something added in a loop and never removed.

Share

More to read

Related posts