You’ve seen the signs: your Node.js API starts fast, but after a few hours, response times creep up. Then the container restarts. You’ve got a memory leak.
The good news? Modern tools make tracking it down far easier than you think.
What You’ll Learn
· How to confirm it’s a leak, not normal growth
· Capturing heap snapshots without stopping your app
· Finding the guilty object in Chrome DevTools
Step 1: Prove It’s a Leak
Run your process with:node --inspect app.js
Open chrome://inspect, attach to your process, and take a heap snapshot. Then force a garbage collection (click the trash can icon), wait 30 seconds, and take another snapshot. If memory doesn’t return to baseline, you have a leak.
Step 2: Compare Snapshots
In DevTools, switch to Comparison view. Look for “size delta” – large positive numbers mean objects aren’t being freed. Expand the constructor list and focus on your own classes or large arrays.
Step 3: Trace the Retainer
Click on a suspicious object. The retainers panel shows why it’s still referenced – often a forgotten event listener, a growing array in a closure, or a global variable.
Common real-world culprits:
· Missing .removeListener() on long-lived EventEmitters
· Caching user data without a TTL or size limit
· Accidentally adding variables to global
Step 4: Fix and Verify
Add process.memoryUsage().heapUsed to a /health endpoint, then load test with autocannon or k6. The fix is confirmed when memory stabilizes after multiple GC cycles.
Pro Tips for Production
· Use node –inspect only temporarily – it has overhead
· For always-on monitoring, try clinic or prom-client with Grafana
· Set up a CI test that fails if heap grows >10% after 1000 requests
Summary
Memory leaks aren’t magic. Heap snapshots + comparison view reveal exactly what’s stuck in memory. Next time your Node.js app gets sluggish, you’ll know exactly where to start.