Memory Management and Garbage Collection
Understanding how JavaScript manages memory
Challenge
Section titled “Challenge”Write memory-efficient code, detect memory leaks, and optimize garbage collection impact.
Explanation
Section titled “Explanation”JavaScript automatically manages memory through garbage collection. Understanding how it works helps write efficient code.
Garbage Collection Basics
Section titled “Garbage Collection Basics”// Objects referenced are kept in memorylet obj = { name: 'John' };
// Unreachable object can be garbage collectedobj = null; // Remove reference
// Objects with circular references are still collectableconst a = { name: 'A' };const b = { name: 'B' };a.ref = b;b.ref = a;
a = null; // When no external reference, both collectedb = null;Memory Leaks
Section titled “Memory Leaks”// ❌ Event listener leakfunction setupButton() { const btn = document.getElementById('btn'); btn.addEventListener('click', () => { // If btn removed from DOM, listener still holds reference console.log('clicked'); });}
// ✅ Fix: Remove listener when not neededfunction setupButton() { const btn = document.getElementById('btn');
const handler = () => console.log('clicked'); btn.addEventListener('click', handler);
btn.addEventListener('remove', () => { btn.removeEventListener('click', handler); });}
// ❌ Accumulating arraysconst listeners = [];function subscribe(callback) { listeners.push(callback); // Never cleared!}
// ✅ Fix: Track and clean upclass EventBus { constructor() { this.listeners = []; }
subscribe(callback) { this.listeners.push(callback);
// Return unsubscribe function return () => { const index = this.listeners.indexOf(callback); if (index > -1) this.listeners.splice(index, 1); }; }}WeakMap and WeakSet
Section titled “WeakMap and WeakSet”// Weak references don't prevent garbage collectionconst cache = new WeakMap();
class User { constructor(id) { this.id = id; }}
const user = new User(1);cache.set(user, { metadata: 'data' });
// When user is garbage collected, cache entry is too// without preventing garbage collectionDetached DOM Nodes
Section titled “Detached DOM Nodes”// ❌ Memory leak: Removed but still referencedconst detachedNode = document.getElementById('old-element');document.body.removeChild(detachedNode);
// detachedNode still holds memory!
// ✅ Fix: Clear referencelet detachedNode = document.getElementById('old-element');document.body.removeChild(detachedNode);detachedNode = null; // Allow GCLarge Objects
Section titled “Large Objects”// ❌ Accumulate large objectsconst cache = [];function cacheResponse(data) { cache.push({ timestamp: Date.now(), data }); // Grows unbounded!}
// ✅ Fix: Implement limitsclass LimitedCache { constructor(maxSize = 100) { this.cache = []; this.maxSize = maxSize; }
add(item) { this.cache.push(item); if (this.cache.length > this.maxSize) { this.cache.shift(); // Remove oldest } }}Closures and Memory
Section titled “Closures and Memory”// ❌ Closure retains unnecessary variablesfunction createHandler(data) { const largeData = new Array(10000).fill(data);
return () => { console.log(data); // But closure retains entire largeData! };}
// ✅ Fix: Only reference what's neededfunction createHandler(data) { return () => { console.log(data); // Only data is retained };}
// ✅ Or explicitly clearfunction createHandler(data) { let largeData = new Array(10000).fill(data); const handler = () => console.log(data); largeData = null; // Clear return handler;}Performance Monitoring
Section titled “Performance Monitoring”// Check memory usage (Chrome DevTools)if (performance.memory) { console.log('Used:', performance.memory.usedJSHeapSize); console.log('Limit:', performance.memory.jsHeapSizeLimit); const percentUsed = (performance.memory.usedJSHeapSize / performance.memory.jsHeapSizeLimit * 100).toFixed(2); console.log(`Heap ${percentUsed}% full`);}Best Practices
Section titled “Best Practices”// 1. Minimize global variables// ❌ const global = { data: largeObject };// ✅ Use modules
// 2. Clean up event listenersbutton.addEventListener('click', handler);// Later...button.removeEventListener('click', handler);
// 3. Cancel async operationsconst controller = new AbortController();fetch(url, { signal: controller.signal });// When done...controller.abort();
// 4. Use WeakMap for cachesconst cache = new WeakMap();
// 5. Avoid circular references where possible