ReviseAlgo Logo

Performance & Optimization

Memory Leaks — Common Causes & Detection

Master memory leak detection in JavaScript. Learn the root causes of memory leaks, detached DOM nodes, uncleared timers, and profiling tools.

Last Updated: July 15, 2026 12 min read

1. Introduction

A Memory Leak occurs when an application allocates memory for objects or data but fails to release it when they are no longer needed. Over time, these leaked objects accumulate, consuming system memory and slowing down or crashing the application.

2. Why It Matters

In Single Page Applications (SPAs) where users can keep a tab open for hours, even a small memory leak (like a few kilobytes per action) can accumulate. This causes the browser tab to slow down, lag, and eventually crash with an "Out of Memory" error.

3. Real-World Analogy

Think of a Hotel Room Occupancy Ledger:

  • Normal Occupancy (GC): Guests check in, use the room, check out, and the hotel registers the room as empty.
  • Memory Leak (Ghost keys): A guest checks out but leaves a suitcase in the closet or retains a duplicate key card. The system ledger still lists the room as "occupied" (reachable reference). Symmetrically, the room cannot be cleaned or rented to new guests, slowly reducing the number of available rooms until the hotel is full.

4. Common Causes of Memory Leaks

Let's look at the four most common ways memory is leaked in JavaScript:

1. Accidental Globals:

Assigning values to undeclared variables attaches them to the global window object, preventing them from being garbage collected.

2. Uncleared Intervals or Timers:

Intervals like setInterval retain references to their callbacks and scope variables in memory until they are explicitly cleared.

3. Detached DOM Nodes:

Removing an element from the DOM tree while retaining references to it in JavaScript variables prevents the element from being garbage collected.

4. Out-of-Scope Closures:

Nested functions that retain references to heavy objects in outer scopes can keep those objects in memory even if they are no longer needed.

5. Practical Example

This script demonstrates a common memory leak in single-page applications: registering an event listener on the global window object but failing to remove it when the component is destroyed:

6. Detecting Memory Leaks with DevTools

To diagnose and locate memory leaks in Chrome DevTools:
1. Open Chrome DevTools (F12) and go to the Performance or Memory tab.
2. Select Heap Snapshot and click Take snapshot.
3. Perform actions in your application (such as opening and closing a modal multiple times).
4. Take a second Heap Snapshot.
5. Set the perspective selector to Comparison to view objects allocated between snapshots.
6. Search for constructor terms like Detached or check for objects that should have been freed but are still present in memory.

7. Common Mistakes

  • Forgetting to clean up subscriptions in event listeners: Event listeners registered on global objects (like window, document, or central state managers) must be removed when the subscribing component is destroyed, otherwise they create persistent memory leaks.

8. Quick Quiz

Q1: Which DevTools feature should you use to compare object allocations over time to identify memory leaks?

A) Console log profiles

B) Heap Snapshot Comparison

Answer: B — Comparing multiple heap snapshots allows you to identify objects that are allocated but never garbage collected.

9. Scenario-Based Challenge

The SPA Search Component Leak:

A search component registers an input change event listener and triggers an API search. When the user navigates away, the component is removed from the DOM, but memory profiles show that the component state is still retained in memory. Write a safe cleanup function to prevent this leak.

10. Debugging Exercise

Explain why this interval timer causes a memory leak, and how to fix it:

class TimerWidget {
  constructor() {
    this.data = new Array(100000);

// Bug: starts interval, but does not save the timer ID! setInterval(() => { this.updateDisplay(); }, 1000); }

updateDisplay() { /* ... */ } }

let widget = new TimerWidget(); // navigates away... widget = null; // Memory leak! Why?

View Solution

Diagnosis: The setInterval callback references the widget instance via this.updateDisplay(). Because the interval is never cleared, the browser's timer system retains a reference to the callback, preventing the widget instance from being garbage collected even after the variable widget is set to null.

Fix: Save the timer ID returned by setInterval, and clear it when the widget is destroyed:

class TimerWidget {
  #timerId;

constructor() { this.data = new Array(100000); this.#timerId = setInterval(() => this.updateDisplay(), 1000); }

updateDisplay() { /* ... */ }

destroy() { clearInterval(this.#timerId); // Clear timer and release references! } }

11. Interview Questions

🟢 Q1: Describe three common patterns that cause memory leaks in JavaScript and explain how to avoid them.

Answer:
1. Uncleared Timers (setInterval/setTimeout): Timers retain references to their callbacks and scope variables in memory until they are cleared.
Prevention: Save the timer ID and call clearInterval or clearTimeout when the component is destroyed.
2. Detached DOM Nodes: Retaining references to deleted DOM elements in JavaScript variables prevents them from being garbage collected.
Prevention: Set variables referencing DOM nodes to null after removing them from the DOM tree.
3. Global Event Listeners: Event listeners registered on global objects (like window or document) retain references to component callbacks.
Prevention: Remove event listeners using removeEventListener when components are destroyed.

12. Production Considerations

  • Automated Leak Testing: Use automated testing tools (like Playwright with heap snapshot checks) in your CI/CD pipeline to detect memory leaks automatically during development.