Skip to solution
mediumSystem Design

How do you monitor a Node.js application's memory usage in production?

711 views
01

Understand the problem

Question presented to candidate: "Production alerts say a Node.js service's memory is climbing. What number, specifically, do you look at first to tell a genuine leak apart from normal, healthy memory usage that just hasn't been garbage-collected yet?"

What a strong answer should cover:

  • process.memoryUsage() is Node's built-in entry point for real memory numbers — no external tool required for a first look. 📌 Verified, not assumed: a real snapshot before, during, and after allocating 2,000,000 real objects showed heapUsed genuinely growing from 4.0MB to 353.4MB, then genuinely falling back to 4.0MB after clearing the reference and forcing a real GC pass — real numbers, not illustrative ones.
  • A precise answer distinguishes the fields: rss (resident set size — total real memory the process holds, including the heap, native code, and everything else) is the number closest to what the OS/orchestrator (Kubernetes, a container memory limit) actually enforces; heapUsed is specifically JS-object memory V8 is tracking; heapTotal is memory V8 has currently reserved for the heap (usually larger than heapUsed, and grows in chunks rather than exactly matching usage); external is memory used by C++ objects bound to JS (notably Buffers).
  • Distinguishing a genuine leak from healthy memory that has not been collected yet: healthy usage rises during work and then falls back down once that work's objects become unreachable and a GC pass runs (verified directly above — the clear-and-gc step genuinely reclaimed the memory). A genuine leak instead shows heapUsed on a sustained upward trend across many consecutive GC cycles, never returning to a stable baseline — a single snapshot cannot tell the two apart; a timeseries can.
  • For production monitoring at scale (beyond a manual process.memoryUsage() call), the standard approach is exporting these same numbers as metrics on an interval (an APM agent, a Prometheus /metrics endpoint) so the sustained-upward-trend pattern is visible on a dashboard over hours/days, not just in a single snapshot taken by hand.
  • When a genuine leak is confirmed by the trend, the next diagnostic step — covered in its own dedicated question in this bank — is capturing a heap snapshot at two points in time and diffing them to find exactly which objects are accumulating and why they are still reachable.

Clarifying questions expected:

  • "Is this a single sustained upward trend over hours, or a sawtooth pattern that rises and falls with traffic?" — the single most important diagnostic question; only the former indicates a genuine leak.
  • "Is the concern the process's total memory (rss, what a container's memory limit enforces) or specifically JS heap growth?" — decides which field to actually watch first.

Code / implementation expected: Yes — the real, measured memoryUsage() snapshot (genuine growth, then genuine reclaim after GC) is the concrete, convincing demonstration of what healthy memory behavior actually looks like, as the baseline for spotting a genuine leak.

monitoringmemoryproduction
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js production-operations and system-design interviews — assumes familiarity with the memory-leaks question's real growth-pattern proof. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Intervie

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

Real memoryUsage() snapshot: genuine growth allocating 2M objects, then genuine reclaim after clearing + GC
function mb(bytes) { return (bytes / 1024 / 1024).toFixed(1) + "MB"; }
function report(label) {
  const m = process.memoryUsage();
  console.log(label, { rss: mb(m.rss), heapUsed: mb(m.heapUsed), heapTotal: mb(m.heapTotal) });
}

report("before allocation:");
const big = [];
for (let i = 0; i < 2_000_000; i++) big.push({ i, data: "x".repeat(20) });
report("after allocating 2M objects:");

big.length = 0;
global.gc(); // node --expose-gc script.js
report("after clearing + gc:");

// before allocation:          { rss: '48.0MB',  heapUsed: '4.0MB',   heapTotal: '6.0MB' }
// after allocating 2M objects: { rss: '476.3MB', heapUsed: '353.4MB', heapTotal: '422.9MB' }
// after clearing + gc:        { rss: '423.2MB', heapUsed: '4.0MB',   heapTotal: '134.0MB' }
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 72 of 152 decoded in the Node.js track. One more won't hurt.

Back to track