Back to Archives
Performance8 min read

Deep-Dive: Node.js Memory Leaks and V8 Heap Profiling

Published on July 24, 2026 by Amrit Sharma

Memory management inside modern runtime environments like Node.js is heavily automated by the V8 garbage collector (GC). However, automation does not mean invulnerability. Developers often face memory growth curves that eventually trigger an Out-Of-Memory (OOM) crash with exit code 137 inside container clusters.

1. Understanding the V8 Heap Structure

V8 divides the memory heap into distinct generational spaces:

  • New Space (Young Generation): A small, highly contiguous space (typically 1-8MB) where new objects are allocated. Scavenge GC operations run here frequently and are extremely fast.
  • Old Space (Old Generation): Holds objects that survived multiple GC sweeps. It is divided into Old Pointer Space and Old Data Space. Mark-Sweep-Compact GC runs here, which is more computationally intensive.
  • Large Object Space: Holds allocations larger than standard spaces limits to avoid moving them around during GC cycles.

2. Common Memory Leak Vectors

Leaks typically arise when references to short-lived objects are held by long-lived scopes:

  1. Accidental Global Variables:
    function processRequest(req) {
      // Missing var/let/const makes this a property of global scope
      requestLog = req.body; 
    }
  2. Retained Event Listeners: Attaching event emitters without removing listeners on cleanup, causing arrays of listeners to grow indefinitely.
  3. Unbounded Caching: Keeping cache objects in global scopes without maximum size thresholds (LRU bounds) or TTL bounds.

3. Taking Heap Snapshots

You can write heap snapshots programmatically inside Node.js for diagnostic profiling:

const v8 = require('v8');
const fs = require('fs');

function writeProfile() {
  const snapshotStream = v8.getHeapSnapshot();
  const fileName = `${Date.now()}.heapsnapshot`;
  const fileStream = fs.createWriteStream(fileName);
  snapshotStream.pipe(fileStream);
  console.log(`Snapshot saved as ${fileName}lank`);
}