Memory & Performance
Memory leaks, garbage collection, rendering optimization, and building fast JavaScript applications
Last updated on
Writing fast JavaScript isn't just about algorithms — it's about understanding how the browser renders, how memory works, and where bottlenecks hide.
Memory Leaks — The Silent Performance Killer
Common Leak Patterns
// 1. Forgotten timers
const timer = setInterval(() => {
fetchAndUpdate();
}, 1000);
// Fix: clearInterval(timer) when done
// 2. Event listeners not removed
element.addEventListener("click", handler);
// Fix: element.removeEventListener("click", handler)
// 3. Closures retaining references
function createLeak() {
const huge = new Array(1_000_000);
return () => huge.length; // closure keeps `huge` alive
}
// 4. DOM references after removal
const el = document.getElementById("temp");
document.body.removeChild(el);
// `el` still references the removed DOM node
// Fix: el = null;
// 5. Global variables
function oops() {
leaked = "I'm global!"; // no const/let/var
}Rendering Performance
The Browser Rendering Pipeline
JavaScript → Style → Layout → Paint → CompositeLayout (reflow) is expensive — avoid triggering it repeatedly:
// ❌ Triggers layout on EVERY iteration (layout thrashing)
for (let i = 0; i < 100; i++) {
const height = element.offsetHeight; // read → triggers layout
element.style.height = height + 1 + "px"; // write → invalidates layout
}
// ✅ Batch reads then writes
const height = element.offsetHeight; // single read
for (let i = 0; i < 100; i++) {
element.style.height = height + i + "px";
}requestAnimationFrame for Animations
// ❌ setInterval for animation — misses frames
setInterval(() => { element.style.left = x++ + "px"; }, 16);
// ✅ requestAnimationFrame — syncs with browser refresh
function animate() {
element.style.left = x++ + "px";
if (x < 500) requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Performance Optimization Checklist
JavaScript
- Use
constandletovervar - Avoid creating objects in hot loops
- Use
Map/Setinstead of plain objects for frequent lookups - Memoize expensive computations
- Use Web Workers for CPU-intensive tasks
DOM
- Minimize DOM access — cache references
- Use
DocumentFragmentfor batch inserts - Use CSS classes instead of inline styles
- Use event delegation instead of many listeners
- Use
textContentoverinnerHTMLwhen possible
Loading
- Lazy loading: Load images/components only when visible
- Code splitting: Load JavaScript modules on demand
- Tree shaking: Remove unused code (bundler feature)
- Defer/async scripts: Don't block HTML parsing
<script src="app.js" defer></script> <!-- deferred: runs after HTML parsed -->
<script src="analytics.js" async></script> <!-- async: runs when ready -->Measuring Performance
// Timing code execution
console.time("operation");
expensiveOperation();
console.timeEnd("operation"); // "operation: 142ms"
// Performance API
const start = performance.now();
expensiveOperation();
const end = performance.now();
console.log(`Took ${end - start}ms`);
// Mark and Measure
performance.mark("start-fetch");
await fetch("/api/data");
performance.mark("end-fetch");
performance.measure("fetch-time", "start-fetch", "end-fetch");Chrome DevTools
- Performance tab: Record and analyze frame rate, CPU usage
- Memory tab: Take heap snapshots, find leaks
- Lighthouse: Automated performance audit
Virtual Scrolling Concept
For lists with thousands of items, render only what's visible:
// Instead of rendering 10,000 list items:
// Only render the ~20 items visible in the viewport
// Recycle DOM nodes as user scrolls
// Libraries: react-virtualized, @tanstack/virtual