Sajith Rahim | Almanac | Blog
 
Dev

Memory Leaks in JavaScript

Sajith AR

Memory leaks occur when unused memory is not released, degrading application performance.

Common Causes and solutions

1. Unintentional Global Variables

Cause : Variables declared without varlet, or const become global.

	
        
    
        
    
    

function createLeak() {
 leakedVar = 'This is a global variable'; // Global scope
}

Solution : Use strict mode or explicit declarations:

	
        
    
        
    
    

"use strict";
function preventLeak() {
 let safeVar = 'Local variable'; // Block-scoped
}

2. Closures Retaining References

Cause : Closures retain outer scope variables unnecessarily.

	
        
    
        
    
    

function outer() {
 const largeData = new Array(1000000).fill('data');
 return function inner() {
 console.log('Closure retains largeData'); // largeData is not GC'd
 };
}

Solution : Nullify references after use:

	
        
    
        
    
    function outer() {
 let largeData = new Array(1000000).fill('data');
 return function inner() {
 console.log('Closure');
 largeData = null; // Release reference
 };
}

3. Timers and Callbacks

Cause : Uncleared setInterval or event listeners.

	
        
    
        
    
    
const intervalId = setInterval(() => {
 // Repeated logic retains references
}, 1000);
// Never cleared → memory leak

Solution : Clean up timers/listeners:

	
        
    
        
    
    
const intervalId = setInterval(...);
clearInterval(intervalId); // Manually clear

// Event listener cleanup:
element.addEventListener('click', handler);
element.removeEventListener('click', handler);

4. Detached DOM Trees

Cause : Removed DOM nodes referenced by JavaScript.

	
        
    
        
    
    
let detachedElement = document.createElement('div');
document.body.appendChild(detachedElement);
document.body.removeChild(detachedElement); // DOM removed
// detachedElement still references the node → not garbage collected

Solution : Nullify references:

	
        
    
        
    
    
detachedElement = null; // Allow GC to reclaim memory

5. Cached Data Accumulation

Cause : Unbounded caches grow indefinitely.

	
        
    
        
    
    
const cache = new Map();
function storeData(key, data) {
 cache.set(key, data); // No size limit → leaks over time
}

Solution : Use WeakMap or limit cache size:

	
        
    
        
    
    
const weakCache = new WeakMap(); // Entries GC'd if keys are unreachable
function storeEphemeralData(key, data) {
 weakCache.set(key, data);
}

// Or enforce size limits:
const MAX_CACHE_SIZE = 100;
function storeLimitedData(key, data) {
 if (cache.size >= MAX_CACHE_SIZE) cache.delete(oldestKey);
 cache.set(key, data);
}

Best Practices with Code Examples

  1. Framework Cleanup (React):
	
        
    
        
    
    
useEffect(() => {
 const subscription = dataStream.subscribe();
 return () => subscription.unsubscribe(); // Cleanup on unmount
}, []);
  1. Weak References:
	
        
    
        
    
    
const weakMap = new WeakMap();
const key = { id: 1 };
weakMap.set(key, 'data'); // Entry removed if key is GC'd
  1. Heap Profiling: Use Chrome DevTools Memory tab to snapshot and compare heap allocations.

CONNECT

 
Have something to share,
Let's Connect