Common Memory Leaks
Common Memory Leaks
Section titled “Common Memory Leaks”Introduction
Section titled “Introduction”Memory leaks happen when the application holds references to memory that’s no longer needed. They cause performance degradation over time.
Global Variables
Section titled “Global Variables”// ❌ Leaks to global scopefunction bad() { data = 'leaked'; // Implicit global!}
// ❌ Accidental global through 'this'function leaky() { this.leak = 'global leak';}leaky(); // 'this' is window!
// ✅ Properly scopedfunction good() { let data = 'scoped'; // Local variable — GC eligible}Forgotten Timers & Intervals
Section titled “Forgotten Timers & Intervals”// ❌ Timer holds reference to DOM elementfunction startTimer() { const element = document.getElementById('heavy'); setInterval(() => { element.innerHTML = Date.now(); // Holds element reference forever! }, 1000);}
// ✅ Clean up timersfunction startSafeTimer() { const element = document.getElementById('heavy'); const timer = setInterval(() => { element.innerHTML = Date.now(); }, 1000); // Store timer reference to clear later return () => clearInterval(timer);}Detached DOM Elements
Section titled “Detached DOM Elements”// ❌ Removed from DOM but still referencedconst detachedElements = [];
function createAndDetach() { const el = document.createElement('div'); el.textContent = 'I am detached'; document.body.appendChild(el); document.body.removeChild(el); // Removed from DOM detachedElements.push(el); // But still referenced! ❌ // Memory leak — DOM elements stay in memory}Closures Holding Large Data
Section titled “Closures Holding Large Data”// ❌ Closure holds entire large array foreverfunction processData() { const largeArray = new Array(1000000).fill('data'); return function() { console.log(largeArray.length); // Holds reference to largeArray };}
// ✅ Clear reference after usefunction processDataSafely() { const largeArray = new Array(1000000).fill('data'); const result = largeArray.length; largeArray = null; // Allow GC return function() { console.log(result); // Only holds a number, not the array };}
// ✅ Or use closure carefullyfunction processDataBetter() { const result = new Array(1000000).fill('data').length; return () => console.log(result);}Detecting Leaks
Section titled “Detecting Leaks”// In Chrome DevTools:// 1. Memory tab → Heap snapshot// 2. Take initial snapshot// 3. Perform actions in the app// 4. Take another snapshot// 5. Compare — look for detached DOM nodes
// Programmatic memory checkif (performance.memory) { console.log(`JS Heap: ${(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}MB`);}Summary
Section titled “Summary”- Global variables are the #1 cause of leaks
- Always clean up timers, intervals, and event listeners
- Detached DOM elements still in array = leak
- Closures can unintentionally hold large data
- Use Chrome DevTools Memory tab to detect leaks