How to Fix a Node.js Memory Leak

Most Node.js memory leaks follow the same pattern: a global Array, Map, or Set that gets appended to on every request but never pruned. The process runs fine at first, then the heap climbs until the OOM killer fires at 3am. Here's how to find it and cap it.

Backend Engineernodememory-leakperformance

Why the heap keeps climbing

Node.js processes are long-lived. Any collection you allocate at the module level and never clear accumulates entries for the entire lifetime of the process. Under production traffic - thousands of requests per hour - an unchecked global array goes from harmless to hundreds of MB in hours.

The classic shape looks like this:

const requests = [];   // global - lives forever

server.on('request', (req) => {
  requests.push({ url: req.url, ts: Date.now() });
  // nothing ever removes entries
});

After 24 hours of normal traffic, requests holds millions of entries. The heap grows linearly with uptime - the textbook sign of an unbounded collection.

Find the leak

Before touching code, confirm WHERE memory is growing. Two fast approaches:

A heap that never stabilizes between requests points to a module-level collection. Scan your top-level code for any const arr = [] or const map = new Map() that is appended to in a request handler.

Cap the collection with .shift()

The simplest fix is a length cap. After each push, drop entries from the front until the array is back inside the limit:

const requests = [];
const MAX_REQUESTS = 100;

server.on('request', (req) => {
  requests.push({ url: req.url, ts: Date.now() });
  while (requests.length > MAX_REQUESTS) requests.shift();
  // array never exceeds 100 entries - O(1) memory
});

Memory growth is now O(1) regardless of traffic volume.

Alternative: time-based pruning

If you need "last N seconds" rather than "last N entries", prune by timestamp:

const WINDOW_MS = 60_000; // keep the last 60 seconds

server.on('request', (req) => {
  const now = Date.now();
  requests.push({ url: req.url, ts: now });
  // drop entries older than the window
  while (requests.length && requests[0].ts < now - WINDOW_MS) {
    requests.shift();
  }
});

Verify the fix

After applying either fix, run a load test and watch /stats:

bash /workspace/load-test.sh       # send 1000 requests
curl -s http://localhost:3000/stats  # should plateau at <= 100

The count should hit the cap and stay flat, not keep climbing.

In production: go deeper

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What causes memory leaks in Node.js?

The most common cause is a global collection (Array, Map, Set, or EventEmitter listener list) that is appended to on every request but never pruned. Because Node processes are long-lived, the collection grows indefinitely until the process is killed by the OOM handler.

How do I fix an unbounded array in Node.js?

Add a length cap after each push - use while (arr.length > MAX) arr.shift() to drop the oldest entry. This keeps memory O(1) regardless of traffic. For time-based expiry, prune entries whose timestamp is older than your window.

How do I find a Node.js memory leak?

Log process.memoryUsage().heapUsed on a timer and look for steady linear growth with no plateau. Then scan module-level code for global arrays or Maps that are written to in request handlers. Tools like clinic.js and heapdump generate V8 heap snapshots to pinpoint the allocation site.

What causes a memory leak in Node.js?

Usually a reference that is never released - a global array or map that grows forever, event listeners added but never removed, or closures holding large objects. The heap climbs until the process is OOM-killed; cap or clear the collection to fix it.

Keep learning

Return the Correct HTTP Status CodeBackend projectRepair Broken API PaginationBackend projectAdd CORS Middleware to an Express APIBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →