How to Find and Fix Memory Leaks in JavaScript

Learn a systematic process for finding and fixing JavaScript memory leaks using Chrome DevTools heap snapshots, the allocation timeline, and the detached DOM node detector.

7 min read

To find memory leaks in JavaScript, start by noticing the symptoms. The page gets slower over time. Memory usage in the task manager climbs without ever dropping.

On mobile, the tab eventually crashes. Your job is to confirm there is a leak, identify what is leaking, trace it to the root cause, and fix the reference that keeps objects alive.

This guide walks through a systematic process using Chrome DevTools. The same approach works in Edge and other Chromium-based browsers.

Step 1: Confirm the Leak

Before diving into heap snapshots, do a quick check with the Performance panel. Open DevTools, go to the Performance tab, and check the Memory checkbox.

Click record and perform the suspected leaking action five to ten times. Stop recording.

Look at the JS heap graph at the bottom. A healthy page shows a sawtooth pattern: memory climbs during each action, then drops back to baseline when garbage collection runs. A leaking page shows the baseline climbing higher after each action, never returning to where it started.

If you see the sawtooth pattern, there is no leak. Move on to other performance work. If the baseline keeps rising, proceed to step 2.

Step 2: Find What Is Leaking

Switch to the Memory tab and select Heap snapshot. Take a snapshot before performing any actions. Perform the suspected action five times.

Take a second snapshot.

Switch the view at the top from Summary to Comparison. Sort by the Delta column to see which objects increased the most. An object type that increased by roughly the same number as your action repetitions is a strong leak candidate.

Click a leaking object and look at the Retainers panel at the bottom. This shows the reference chain from a GC root to this object. The shortest path is usually the one preventing collection.

Follow it up to find the root cause.

Step 3: Trace the Retainer Chain

The retainer chain answers the question: why is this object still in memory? It reads from bottom to top, with the object itself at the bottom and the root reference at the top.

A typical retainer chain for a leaked object might look like: the object is held by a closure, the closure is held by an event listener, the event listener is attached to a DOM element, the DOM element is in the document. The fix is to remove the event listener when the element is no longer needed.

A retainer chain starting from a setInterval callback means the interval was never cleared. The fix is to call clearInterval when the component unmounts or the page navigates away.

For common leak patterns and their fixes, see /javascript/fixing-javascript-memory-leaks-complete-guide.

Step 4: Use the Allocation Timeline

If heap snapshots do not clearly show the problem, try the allocation timeline. Select Allocation instrumentation on timeline in the Memory tab. Click record, perform the leaking action once, and stop recording.

The timeline shows bars representing allocations over time, colored by constructor. Look for large bars that appear during the action. Click a bar to see exactly which objects were allocated at that moment and what code created them.

The timeline also shows a memory graph. If memory climbs during the action and does not drop back down, you have a leak.

The bars show what was allocated. The retainer chain shows why it was kept.

Step 5: Find Detached DOM Nodes

Detached DOM nodes are elements removed from the document but still referenced by JavaScript. They are a common leak in single-page applications.

In the Memory tab, take a heap snapshot. In the class filter at the top, type Detached. The results show every detached HTML element still in memory.

Click one and check the Retainers panel to find the JavaScript variable holding the reference.

Chrome DevTools also has a dedicated detached elements view inside the Memory panel. Open the Command Menu (Cmd+Shift+P), type "detached", and select it, then click "Get detached elements". This lists every detached DOM node with a preview of the element and its retaining JavaScript reference.

For more on identifying detached nodes specifically, see /javascript/identifying-detached-dom-elements-in-javascript.

Step 6: Fix and Verify

Once you identify the retainer chain, the fix is usually straightforward. Clear the interval, remove the event listener, set the reference to null, or restructure the code to avoid the leak pattern.

After making the fix, verify by repeating the Performance panel check. Record the action again and confirm the heap baseline returns to the starting level after each GC cycle. If the sawtooth pattern appears, the leak is fixed.

Automating Leak Detection

For production applications, automate leak detection. The PerformanceObserver API can report long tasks and memory pressure. The measureUserAgentSpecificMemory API, available in cross-origin isolated contexts, reports detailed memory breakdown.

In Node.js, the process.memoryUsage API provides heap statistics. Track heapUsed over time. If it climbs monotonically without garbage collection drops, investigate.

Integration tests can include memory assertions. After performing an action and waiting for GC, assert that certain objects are no longer referenced. This catches leaks before they reach production.

Rune AI

Rune AI

Key Insights

  • The Performance panel with Memory checkbox gives a quick leak/no-leak answer.
  • Heap snapshot comparison shows exactly which objects are accumulating.
  • The retainer chain reveals the reference path preventing garbage collection.
  • Common fixes: clear intervals, remove event listeners, null out references.
  • Automate leak detection with the detachment detector and performance observer.
RunePowered by Rune AI

Frequently Asked Questions

What is the fastest way to check for memory leaks?

Open the Performance panel, enable the Memory checkbox, and record while performing the suspected leaking action several times. If the heap size baseline keeps climbing without returning to the starting level, you have a leak.

How do I know which object is causing a leak?

Take two heap snapshots, perform the suspected action between them, and compare using the Comparison view. Look for objects with a delta count proportional to the number of repetitions. The Retainers panel shows the reference chain keeping each object alive.

Conclusion

Finding memory leaks is a process, not a guess. Take a heap snapshot before the suspected action, perform the action multiple times, take another snapshot, and compare. The objects that multiplied are your leak candidates. Follow the retainer chain to the root reference. Fix by removing that reference when it is no longer needed.