Memory Leaks in JavaScript

Difficulty: Advanced

Question

What causes memory leaks in JavaScript? How do you detect and prevent them?

Answer

Memory leaks occur when the garbage collector cannot reclaim memory because objects are still referenced but no longer needed.

Common causes: 1. Forgotten event listeners: adding listeners without removing them 2. Closures holding references: closures capturing large objects unnecessarily 3. Detached DOM nodes: DOM elements removed from the tree but still referenced in JS 4. Global variables: accidentally creating globals accumulate over time 5. Timers and intervals: setInterval callbacks referencing objects 6. Caches without eviction: unbounded Maps/objects as caches

Detection: Chrome DevTools Memory tab (heap snapshots).

Code examples

Event Listener Leak and Fix

class ComponentFixed {
  constructor() {
    this._handler = this.handleResize.bind(this);
  }

  mount() {
    window.addEventListener('resize', this._handler);
  }

  unmount() {
    window.removeEventListener('resize', this._handler);
  }

  handleResize() {
    console.log('Resized');
  }
}

// In React: return cleanup from useEffect
// useEffect(() => {
//   window.addEventListener('resize', handler);
//   return () => window.removeEventListener('resize', handler);
// }, []);

The bound method reference must be stored to remove the same function that was added. Without cleanup, each mount adds another listener.

Timer Leak and Detached DOM

function setupLeak() {
  const hugeData = new Array(1000000).fill('data');
  // setInterval holds hugeData alive indefinitely
  setInterval(() => {
    console.log('tick', hugeData.length);
  }, 1000);
}

// FIX: store and clear interval ID
function setupFixed() {
  const id = setInterval(() => {
    console.log('tick');
  }, 1000);
  return () => clearInterval(id);
}

// Detached DOM node
let cache = {};
function fixLeak() {
  const el = document.createElement('div');
  document.body.appendChild(el);
  document.body.removeChild(el);
  cache.el = null; // Release reference
}

Closures in timers hold strong references to captured variables. Removing a DOM node from the document does not free memory if JS still references it.

Key points

Concepts covered

Memory Leak, Garbage Collection, Event Listeners, Closures, Detached DOM