Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
A memory leak occurs when your application keeps holding onto memory that's no longer needed. Unlike languages with manual memory management, JavaScript has automatic garbage collection - but that doesn't mean leaks can't happen! When objects remain referenced when they shouldn't be, garbage collection can't free that memory, causing your app to consume more and more RAM over time.
Step 1: Open DevTools → Memory tab
Step 2: Take a heap snapshot
Step 3: Use your app normally (interact with it)
Step 4: Take another heap snapshot
Step 5: Compare the two snapshots
What to look for:
✅ Normal Memory (Healthy)
Memory goes up (allocations), then down (garbage collection). Creates a saw-tooth pattern: ↗↘↗↘↗↘
❌ Memory Leak (Unhealthy)
Memory keeps going up and never comes back down. Creates continuous growth: ↗↗↗↗↗↗
Event listeners hold references to DOM elements and callback functions. If you don't remove them when done, they prevent garbage collection.
❌ Memory Leak
function setup() {
const button = document.getElementById('btn');
button.addEventListener('click', handler);
// Never removed!
}✅ Fixed
function setup() {
const button = document.getElementById('btn');
button.addEventListener('click', handler);
// Cleanup when done
return () => {
button.removeEventListener('click', handler);
};
}setTimeout and setInterval create references that persist until explicitly cleared. Forgetting to clear them causes leaks.
❌ Memory Leak
function startPolling() {
setInterval(() => {
fetchData();
}, 1000);
// Runs forever, never stopped!
}✅ Fixed
function startPolling() {
const intervalId = setInterval(() => {
fetchData();
}, 1000);
return () => clearInterval(intervalId);
}DOM nodes removed from the page but still referenced in JavaScript can't be garbage collected.
❌ Memory Leak
const elements = [];
function addElement() {
const div = document.createElement('div');
document.body.appendChild(div);
elements.push(div); // Still referenced!
}
function removeAll() {
document.body.innerHTML = '';
// elements array still holds references
}✅ Fixed
const elements = [];
function addElement() {
const div = document.createElement('div');
document.body.appendChild(div);
elements.push(div);
}
function removeAll() {
document.body.innerHTML = '';
elements.length = 0; // Clear references
}Objects stored in global scope or window object never get garbage collected. Continuously adding to them creates leaks.
❌ Memory Leak
window.cache = {};
function loadUser(id) {
fetch(`/users/${id}`)
.then(r => r.json())
.then(user => {
window.cache[id] = user;
// Cache grows forever!
});
}✅ Fixed (LRU Cache)
const cache = new Map();
const MAX_SIZE = 100;
function loadUser(id) {
fetch(`/users/${id}`)
.then(r => r.json())
.then(user => {
if (cache.size >= MAX_SIZE) {
const first = cache.keys().next().value;
cache.delete(first);
}
cache.set(id, user);
});
}Closures capture their surrounding scope. Large objects captured in closures can't be garbage collected even if you only need a small part.
❌ Memory Leak
function createHandler(data) {
// data is huge object with 1000 properties
return () => {
// Only uses data.id
console.log(data.id);
// But entire 'data' object is captured!
};
}✅ Fixed
function createHandler(data) {
// Extract only what you need
const id = data.id;
return () => {
// Only captures 'id', not entire object
console.log(id);
};
}Remove event listeners, clear timers, cancel subscriptions, and null out references when components unmount or are no longer needed.
For caching with DOM elements or objects, use WeakMap/WeakSet. They allow garbage collection when objects are no longer referenced elsewhere.
If caching data, implement size limits (LRU cache) or time-to-live (TTL) to prevent unbounded growth.
Use Chrome DevTools Memory tab to take heap snapshots periodically. Compare them to catch leaks early in development.
Extract only the data you need from large objects before creating closures. Don't capture entire objects unnecessarily.
Memory that can't be freed
Caused by lingering references
Grows continuously over time
Use DevTools Memory tab
Compare heap snapshots
Look for continuous growth
Event listeners not removed
Timers not cleared
Detached DOM, global accumulation
Always clean up resources
Use WeakMap/WeakSet
Limit cache sizes