Memory Leak and Heap Growth Diagnosis
Memory leak diagnosis starts by asking which memory is growing under which workload, and whether that growth should end. A rising chart can represent retained objects, a useful cache, larger in-flight work, native allocations, or a runtime keeping committed pages for reuse.
Compare equivalent workload phases, identify the growing object population, and trace why it remains reachable. Increasing the heap limit can postpone failure; it does not explain the reference that keeps yesterday’s requests alive.
TL;DR
- Separate managed heap, external allocations, and process resident memory.
- Compare repeated workload cycles after warm-up with the same concurrency and data shape.
- Use allocation profiles to find creation sites and retaining paths to explain survival.
- Treat snapshots as sensitive, potentially disruptive diagnostic artifacts.
- Verify a fix by repeating the workload and checking retained populations and service behavior.
Quick Example
Save as retention.cjs and run with node --expose-gc retention.cjs in a disposable local process. This complete bounded example deliberately retains 2,000 fictional request objects and then releases the map.
Counts demonstrate the reference lifecycle. Byte totals are observations from that process, not fixed assertions or a production leak threshold. Forced garbage collection is a fixture convenience, not a proposed request-path treatment.
Core Concepts
Allocation and retention
An allocation profile answers where objects were created over an interval. A heap snapshot describes reachable heap objects at capture time; retaining paths explain references preventing reclamation. A hot allocation site may produce short-lived objects correctly, while a small global registry retains them indefinitely.
Heap and process memory
Node exposes heapUsed, heapTotal, external, arrayBuffers, and rss with different meanings. Array-buffer memory is included within external memory, so do not add every field as independent usage. RSS covers more than the JavaScript heap. Node process memory accounting
Expected working set
A cache populated by new keys may grow until its configured bound. A backlog may retain live request data until processing catches up. Both can cause an outage even if their references follow the intended code. The diagnosis must distinguish an incorrect lifetime from an insufficiently bounded design.
Follow a repeatable investigation
First identify the failure: process termination, container limit, rising pause time, allocation failure, or host pressure. Preserve the clock, runtime version, workload, concurrency, request size, and relevant memory measurements. Different clocks or processes can produce a convincing but false trend.
Warm up the service, repeat a bounded workload, let in-flight work finish, and compare corresponding points. Track object counts or cache entries alongside memory. If the count grows with every completed request and never expires, inspect ownership and cleanup. If it stabilizes, extend the test enough to challenge the intended bound before calling it healthy.
Snapshots need an operational plan. Node warns that capture stops main-thread work and can substantially increase memory demand, potentially crashing the process. Use a disposable or safely isolated instance with headroom and a recovery path. Snapshots can contain tokens and application data; restrict access and retention. Node heap snapshot guidance
Compare captures from equivalent phases and trace a growing population to a root. In a browser, detached DOM nodes may be retained by listeners or closures. In a server, timers, request registries, and caches deserve the same ownership questions. Follow the actual references rather than blaming the framework named in the stack.
Verify the lifetime repair
Make the smallest change that corrects ownership: unsubscribe at the documented lifecycle boundary, remove completed work from the registry, bound a cache, or cancel obsolete asynchronous work. Confirm cleanup also happens on errors and cancellation. A successful response path is only one way a resource can finish.
Repeat the original workload and the failure path that previously retained data. Check object population, throughput, latency, and error behavior together. A map that stops growing because every request now fails is not a successful repair. Keep the original reproducer as a regression test when it exercises the real lifetime boundary.
The local map example proves only that the illustrated references can be released. It does not identify a leak in Angular, Node, or any application, and it does not measure browser memory, native libraries, or container behavior.
Best Practices
Preserve comparable evidence
Record workload and phase with every capture. Comparing a busy peak with an idle process can identify more live objects without identifying a leak.
Fix ownership before tuning limits
Use size limits and concurrency bounds where they express intended behavior. Adjust memory budgets only after understanding what must remain live and validating the resulting latency and recovery behavior.
Handle diagnostics as application data
Keep captures out of public issue attachments and ordinary logs. Share only the minimum evidence needed through an approved channel.
Common Mistakes
Bad: Calling any upward RSS slope a JavaScript leak.
Correct: Separate memory categories and compare retained heap populations under equivalent workload phases.
Bad: Taking repeated snapshots on the only healthy instance.
Correct: Plan capture overhead, isolation, data handling, and recovery first.
Bad: Clearing a cache without checking correctness.
Correct: Verify the new lifetime preserves required results and remains bounded under repeat and failure cases.
FAQ
Does garbage collection prevent leaks?
It reclaims unreachable managed objects. Objects that remain reachable through an unintended root can still accumulate.
Should RSS immediately fall after releasing objects?
Do not require that as a universal rule. Use runtime-specific accounting, object evidence, and repeated workload behavior.
What confirms the fix?
The original retention pattern stops under comparable repeated work, while correctness and service objectives still hold.
Related Topics
- Profiling & Diagnostics — Choose measurements that discriminate causes.
- Garbage Collection — Understand reachability and reclamation.
- Angular — Review component and subscription lifetimes.