Performance Hands-On 12 - Memory Profiling

Exercise 1: Identifying Memory Leaks

Memory leaks occur when your application retains references to objects that are no longer needed, preventing garbage collection.

Steps to identify memory leaks:

  1. Enable the "Memory" checkbox in the Performance panel
  2. Start recording, then click the "Create Memory Leak" button several times
  3. Observe the JS Heap graph - a leak will show as a steadily increasing line that doesn't drop
  4. Try clicking "Clear Array" and notice if memory is actually released

Current memory usage: 0.00 MB

Memory Leak Solution:

The issue is that we're storing large arrays in the global leaks array. Even when we try to clear it, we're not properly dereferencing the objects:

// Problem - references are kept even after clearing
const leaks = [];
document.getElementById('clearLeaks').addEventListener('click', () => {
  while(leaks.length > 0) {
    leaks.pop(); // Elements are removed but references might persist
  }
});

// Better solution
document.getElementById('clearLeaks').addEventListener('click', () => {
  // Set length to zero to clear array completely
  leaks.length = 0;
  // Or reassign to empty array
  // leaks.splice(0, leaks.length);
});

The key is to ensure all references to the large objects are removed so the garbage collector can reclaim the memory.

Exercise 2: DOM Manipulation Optimization

Inefficient DOM manipulation can cause poor performance and high memory usage due to layout thrashing.

Performance analysis steps:

  1. Start a recording in the Performance panel
  2. Click "Inefficient DOM Manipulation" button
  3. Stop recording and examine the timeline
  4. Look for long tasks and frequent layout recalculations
  5. Now try the "Optimized DOM Manipulation" and compare results

DOM Performance Solution:

The inefficient approach inserts elements one at a time, causing multiple reflows and repaints:

// Inefficient approach
for (let i = 0; i < 500; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  container.appendChild(div); // Causes a reflow each time
}

The optimized solution uses a document fragment to batch DOM operations:

// Efficient approach
const fragment = document.createDocumentFragment();
for (let i = 0; i < 500; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  fragment.appendChild(div); // No reflow here
}
container.appendChild(fragment); // Only one reflow

Using document fragments or setting innerHTML once with a complete string can significantly improve performance for large DOM updates.

document.createDocumentFragment() creates a lightweight container that stores DOM nodes without being part of the actual DOM tree. When you append it to the DOM, only its contents are inserted, not the fragment itself. This prevents multiple reflows because all changes happen in memory first, then are applied to the DOM in a single operation.

Memory Performance Best Practices