To find a Node.js memory leak in production, first track the different memory fields over comparable workloads, then use garbage-collection behavior to test the leak hypothesis. If the trend points to retained JavaScript objects, compare heap snapshots around a controlled action—but capture one only from an instance that can safely crash. Fix the retaining behavior and repeat the same workload to verify the trend changes.
1. Establish which memory is growing
Record a time series rather than diagnosing from one reading. Node.js process.memoryUsage() reports several distinct measurements:
As an Amazon Associate I earn from qualifying purchases.
heapUsedandheapTotal: memory used and allocated by the V8 JavaScript heap.external: memory used by C++ objects associated with JavaScript objects.arrayBuffers: memory forArrayBufferandSharedArrayBufferallocations, including Node.js Buffers. This value is included inexternal.rss: resident memory for the whole process, including JavaScript and native objects and code.
Log these measurements with workload context, process restarts, and deployment changes. Compare similar traffic and time windows, and account for startup and warm-up. The Node.js Process API notes that process.memoryUsage() iterates over memory pages and can be slow depending on allocation patterns; if you need only resident memory, process.memoryUsage.rss() is documented as faster.
Interpret the pattern, not just the number
A repeatable rise in heapUsed across comparable workloads is more suggestive of JavaScript retention than an isolated RSS increase. RSS can rise for other reasons: Node.js documents that allocator fragmentation on glibc systems can cause sustained RSS growth even when heapTotal is stable. If heap measurements are steady but RSS is not, include native and external allocations and allocator behavior in the investigation rather than assuming a JavaScript leak.
#1 Best Overall
2. Check whether garbage collection supports the leak hypothesis
GC traces help determine whether memory is being reclaimed, but they do not identify the retaining code by themselves. The Node.js GC traces guide describes continued old-space growth with little memory reclaimed over repeated collections as a likely leak signal.
Look for that pattern over time and under repeatable load. One trace or one high-water mark is not conclusive: startup allocations, a temporary workload peak, native memory, and allocator fragmentation can all affect readings. The constrained-heap sizes used in a tutorial exercise are diagnostic aids, not production memory limits.
Rank #2
3. Capture incident context with diagnostic reports
A diagnostic report can preserve JavaScript and native stacks, heap information, platform details, and resource usage around an incident. Node.js supports reports triggered by fatal errors, uncaught exceptions, signals, and APIs; see the Node.js Diagnostic Report API for configuration and trigger options.
Use reports to add context to an incident, not as a replacement for a memory time series or an object-retention comparison. Review what operational data the report contains and restrict its storage and access according to your service’s controls.
Rank #3
4. Compare heap snapshots around a controlled action
Snapshots are useful when the evidence points to JavaScript objects that remain reachable. Warm up the service, exercise the suspected feature, capture a baseline, repeat the same activity, and compare a later snapshot with the first. Keep unrelated activity out of the comparison where possible so that object changes from other requests do not obscure the signal.
- Allow startup and normal warm-up to finish.
- Exercise the suspected feature in a focused, repeatable way.
- Capture the first heap snapshot after that activity.
- Repeat the same activity under comparable conditions.
- Capture a second snapshot and compare the newer snapshot against the older one in Chrome DevTools.
- Inspect large positive object deltas and follow their retaining references toward the application behavior keeping them reachable.
The Node.js heap snapshot guide describes this compare-and-inspect workflow. A positive delta is a lead, not proof on its own; repeat with a focused workload to see whether the same retained objects continue to accumulate.
Rank #4
5. Protect production availability before taking a snapshot
Heap snapshot generation is synchronous and blocks other work on the main thread. It may take more than a minute, and building the snapshot in memory can approximately double heap use, potentially exhausting available memory and crashing the process. The Node.js Learn guide warns: “If you’re going to take a heap snapshot in production, make sure the process you’re taking it from can crash without impacting your application’s availability.”
- Choose an instance whose failure will not compromise service availability.
- Do not trigger a snapshot on a critical instance just to get a quicker answer.
- If an HTTP endpoint triggers snapshots, protect it from unauthorized callers.
- Treat snapshot files as sensitive operational data and restrict access to them.
6. Trace retained objects to code and fix the behavior
Use positive object deltas and retaining paths to turn a snapshot into a code-level investigation. Ask whether a collection can grow without bounds, whether listeners or timers are removed when no longer needed, and whether caches or request-scoped data remain reachable longer than intended. These are debugging questions to test against the retaining path, not assumptions about the cause.
If Node.js emits MaxListenersExceededWarning, inspect listener registration and cleanup. The Process API says this warning is often an indication of a memory leak, but the warning alone does not establish that a leak is present.
7. Verify the fix under comparable conditions
After changing the code, repeat the same workload and monitoring window used for the baseline. Compare the memory fields and GC behavior, and check whether the suspected object delta still grows. If V8 heap measurements stabilize but RSS remains elevated, continue examining external or native allocations and allocator behavior; a persistently high RSS value alone does not show that the JavaScript retention problem remains.
Quick Recap
Which diagnostic should you use?
| Method | What it helps reveal | Operational cost and limits |
|---|---|---|
| Memory time series | Whether heap, external, array-buffer, or resident memory is trending upward. | Low disruption when sampled thoughtfully; does not identify retained objects. |
| GC traces | Whether old-space growth is being reclaimed over repeated collections. | Useful as a leak signal, but does not identify the retaining object or code. |
| Heap snapshots | Object deltas and retaining references between two points in time. | Blocks main-thread work, may take more than a minute, and can approximately double heap requirements, creating crash risk. |
| Diagnostic reports | Runtime, stack, heap, platform, and resource context around a trigger. | Useful incident evidence, but not a time series or heap-delta comparison. |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.




