Skip to the solution
MediumSystem Design5 min read

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

711 views

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.

  • monitoring
  • memory
  • production

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

5 min read Β· 8 sections

Read the code

JavaScriptReal 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' }

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 72 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

How do you containerize a Node.js app with a small, secure Docker image (multi-stage, distroless)?

Continue