Course contentsShow
← Node.js
Lesson 1872 of 1,98017. Performance Tuning and ProfilingFree lesson

Identifying Memory Leaks via Heap Snapshots

You will perform comparative analysis of heap snapshots to locate growing object references.

In this lesson, you will learn how to identify memory leaks by performing comparative analysis on V8 heap snapshots to pinpoint objects that persist longer than they should.

When a memory leak occurs in Node.js, it is rarely a sudden crash; instead, it is a slow accumulation of objects that the Garbage Collector (GC) cannot reclaim because they remain reachable via active references. To detect this, we use Heap Snapshot Comparison.

The Comparative Workflow

The secret to effective debugging isn't just looking at one snapshot—it is observing the delta between two states. Follow this process:

  1. Baseline Snapshot: Take an initial snapshot (heapdump) when your application is idle and stable.
  2. Stress Phase: Exercise the specific operation or endpoint you suspect is leaking for a few minutes.
  3. Recovery Snapshot: Trigger a manual GC (if possible) or wait for a pause, then take a second snapshot.
  4. Comparison: In Chrome DevTools, switch the "Comparison" view to show the delta between your Baseline and Recovery snapshots.

Identifying the Leak

When looking at the comparison view, focus on the Delta column. We are looking for objects with a positive delta—specifically, types that have increased in count or total size.

Consider this classic pattern of a leaking EventEmitter listener:

// A hidden leak: attaching a listener in a loop without removing it
server.on('request', (req, res) => {
  const context = { data: new Array(1000).fill('leak') };
  const handler = () => { console.log(context); };
  
  // Every request adds a new closure that retains 'context'
  eventBus.on('processed', handler); 
  res.end('done');
});

In your heap comparison, you would see an increasing count of Closure or Context objects associated with eventBus. By expanding these entries, the Retainers tree shows exactly which object is holding onto the reference (the eventBus instance in this case).

The goal is to find objects that should have been collected but appear in the second snapshot with a high "Delta" count. If an object is not supposed to persist between requests, its delta should be zero.

Key Takeaway: Memory leaks are identified by finding objects with a positive delta in a comparative heap snapshot, then using the Retainers tree to identify the specific object path preventing garbage collection.