What This Means
Memory leaks occur when JavaScript applications retain references to objects that are no longer needed, preventing the garbage collector from freeing memory. Over time, this causes:
- Increasing memory usage that never gets released
- Page slowdown and degraded performance
- Browser tab crashes from out-of-memory errors
- Reduced battery life on mobile devices
- Poor user experience in single-page applications (SPAs)
Common causes of memory leaks:
- Forgotten event listeners not removed on cleanup
- Detached DOM nodes still referenced in JavaScript
- Global variables accumulating data
- Closures capturing large objects
- Timers and intervals not cleared
- Circular references preventing garbage collection
How to Diagnose
Chrome DevTools Memory Panel
- Take heap snapshots before and after using features
- Compare snapshots to find retained objects
- Use Allocation Timeline to track memory over time
- Record Allocation Instrumentation to find allocation sources
Memory Profiling Steps
// Monitor memory usage
if (performance.memory) {
console.log('Used JS Heap Size:',
(performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB');
console.log('Total JS Heap Size:',
(performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB');
}
Common Leak Patterns
- Memory consistently increasing over time
- Detached DOM tree count growing
- Event listener count increasing
- Large arrays or objects never released
Task Manager
- Chrome: Shift+Esc to open Task Manager
- Monitor JavaScript Memory column
- Look for continuous growth without stabilization
General Fixes
Remove event listeners on cleanup
// Bad element.addEventListener('click', handler); // Good useEffect(() => { element.addEventListener('click', handler); return () => element.removeEventListener('click', handler); }, []);Clear timers and intervals
const timerId = setInterval(callback, 1000); // Later... clearInterval(timerId);Avoid global variables - Use modules and local scope to limit lifetime
WeakMap/WeakSet for caches - Allow garbage collection of keys
// Good for caching DOM elements const cache = new WeakMap(); cache.set(element, data);Nullify large object references - Explicitly set to null when done
let largeObject = /* ... */; // Use largeObject... largeObject = null; // Allow GCUnsubscribe from observables - Clean up RxJS, Redux subscriptions
const subscription = observable.subscribe(handler); subscription.unsubscribe();Use AbortController for fetch - Cancel and clean up requests
const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort(); // Clean upAvoid detached DOM nodes - Remove all references when removing elements
Implement proper React cleanup - Return cleanup functions from useEffect
Platform-Specific Guides
| Platform | Guide |
|---|---|
| React | Memory Leaks in React |
| Angular | Unsubscribe Pattern |
| Vue | Lifecycle Cleanup |
| Redux | Unsubscribe from Store |
Further Reading
- Chrome DevTools: Memory Problems
- MDN: Memory Management
- 4 Types of Memory Leaks
- JavaScript Memory Profiling
- Fixing Memory Leaks in Web Apps