How to Fix Memory Leaks in JavaScript (With Examples)

Memory leaks in JavaScript are more common than most developers realize. They cause slow performance, increased memory usage, and, eventually, browser crashes or Node.js process failures. Memory management isn't optional if you're working on long-running applications—single-page apps, real-time dashboards, or APIs.

This guide shows you how to find and fix JavaScript memory leaks, with working examples you can apply today.


What Is a Memory Leak in JavaScript?

A memory leak happens when your code keeps a reference to data you no longer need. JavaScript’s garbage collector frees up memory when an object is no longer reachable. But if you accidentally keep references alive, the memory stays allocated, and the garbage collector skips it.


Why Memory Leaks Matter

  • Performance degrades over time
  • Apps slow down or freeze after prolonged use
  • Server processes consume more memory, causing higher cloud costs or crashes
  • Hard-to-debug bugs creep in as systems get stressed
Understanding Memory leaks in JavaScript

How to Spot a Memory Leak in JavaScript

  1. Consistent memory growth over time
    If memory usage keeps climbing and never drops (even after removing elements or navigating away from pages), you probably have a leak.
  2. Frequent garbage collection without memory drops
    You might notice the garbage collector is running more often, but your memory usage isn’t decreasing.
  3. Performance profiling in DevTools or Node.js shows retained objects
    Detached DOM nodes, closures, and listeners can stick around when they shouldn’t.

Tools to Detect JavaScript Memory Leaks

Chrome DevTools

  • Performance Tab → Record and look for memory usage over time
  • Memory Tab → Take Heap Snapshots to see what’s holding references
  • Allocation Instrumentation → Tracks what’s being allocated and retained

Node.js

  • --inspect flag + Chrome DevTools
  • clinic.js for automated profiling
  • heapdump + memwatch-next to capture and inspect heap snapshots manually

Common Memory Leak Patterns (With Fixes)

1. Accidental Global Variables

Global variables persist for the entire session. If you forget let, const, or var, you’ve created a global without realizing it.

Bad Code

function doSomething() {
  leaked = 'oops'; // Implicit global
}

Fix

function doSomething() {
  const safe = 'fixed';
}

Pro Tip

Use 'use strict'; at the top of your scripts. It’ll throw errors on accidental globals.


2. Timers and Intervals Not Cleared

setInterval and setTimeout hold references in their callbacks. If you don’t clear them, they’ll keep running even after the object they refer to is gone.

Bad Code

function startPolling() {
const element = document.getElementById('data');
setInterval(() => {
element.innerText = 'Polling...';
}, 1000);
}

Even if element is removed from the DOM, setInterval keeps it alive.

Fix

function startPolling() {
const element = document.getElementById('data');
const interval = setInterval(() => {
if (!document.body.contains(element)) {
clearInterval(interval);
return;
}
element.innerText = 'Polling...';
}, 1000);
}

Pro Tip

Always clearInterval and clearTimeout in cleanup code. If you’re in a framework like React or Vue, use the unmount lifecycle hooks.


3. Closures Retaining Unneeded Data

Closures are a common source of leaks when they capture large objects or DOM nodes you no longer need.

Bad Code

function outer() {
const hugeArray = new Array(1000000).fill('data');

return function inner() {
console.log(hugeArray[0]);
};
}

const leaky = outer();

hugeArray stays in memory as long as inner is reachable.

Fix
Null out large references if they’re not needed anymore.

function outer() {
let hugeArray = new Array(1000000).fill('data');

const inner = () => {
console.log(hugeArray[0]);
};

hugeArray = null; // Release reference

return
inner;
}

const fixed = outer();


4. Detached DOM Nodes Still Referenced

If you remove a DOM node from the page but keep a reference to it, it won’t be garbage collected.

Bad Code

let elements = [];

function addElement() {
const el = document.createElement('div');
document.body.appendChild(el);
elements.push(el);
}

function removeElement() {
const el = elements.pop();
document.body.removeChild(el);
// Still held by el and elements array
}

Fix
Break all references after removing nodes.

function removeElement() {
const el = elements.pop();
document.body.removeChild(el);
// Release reference
el = null
;
}

Better yet, clean up arrays entirely if the references are no longer useful:

elements.length = 0;


5. Event Listeners Not Removed

Adding event listeners is easy. Forgetting to remove them when the element is gone is how leaks happen.

Bad Code

const button = document.getElementById('myButton');

function handleClick() {
console.log('clicked');
}

button.addEventListener('click', handleClick);
// button gets removed later without cleanup

Fix
Explicitly remove the listener when cleaning up:

button.removeEventListener('click', handleClick);

Or, use { once: true } when adding the listener:

button.addEventListener('click', handleClick, { once: true });


6. Unbounded Caches or Data Stores

If you’re caching things without limits, you’ll eventually hit memory problems.

Bad Code

const cache = {};

function getData(key) {
if (!cache[key]) {
cache[key] = fetchData(key);
}
return cache[key];
}

Every key you store keeps memory alive indefinitely.

Fix
Use WeakMap for object keys, or implement an LRU cache with size limits.

WeakMap example (keys must be objects):

const cache = new WeakMap();

function getData(obj) {
if (!cache.has(obj)) {
cache.set(obj, fetchData(obj));
}
return cache.get(obj);
}


DOM nodes not garbage collected

Prevent Memory Leaks: Code Patterns and Practices

  1. Use let and const, never var
  2. Keep scopes small
    Minimize long-lived closures and large function scopes
  3. Use WeakMap and WeakSet for object references that shouldn’t prevent garbage collection
  4. Always clear timers and intervals
  5. Remove event listeners when the target node is removed
  6. Detach and dereference DOM nodes
    Don’t leave references hanging around in arrays, maps, or properties
  7. Profile regularly, not just when things go wrong
    Use Chrome DevTools or Node.js profilers as part of your workflow
  8. Limit cache sizes, and clean up old entries
    LRU and TTL strategies work well here

Diagnosing and Fixing Memory Leaks in DevTools (Step by Step)

In Chrome DevTools:

  1. Open Memory tab
  2. Click Take Snapshot
  3. Interact with your app (add/remove elements, navigate)
  4. Take another snapshot
  5. Compare heap differences
    • Look for objects that shouldn’t still be alive
    • Look for Detached DOM trees or listeners still attached to removed nodes

In Node.js:

  1. Start your app with node --inspect
  2. Open chrome://inspect in Chrome
  3. Look for memory snapshots, retained objects
  4. Force garbage collection (global.gc(), but only works with --expose-gc flag)

TL;DR

  • Memory leaks in JS happen when objects stick around longer than they should
  • Watch out for globals, closures, event listeners, and timers
  • Clean up your DOM nodes and break references
  • Use WeakMap and WeakSet when holding onto objects
  • Regular profiling and testing is the only way to keep your app clean