JavaScript manages memory automatically through garbage collection (GC). Developers don’t allocate or free memory manually, but must avoid patterns that prevent GC from reclaiming unused objects.

Memory Lifecycle

  1. Allocate — memory assigned when creating values
  2. Use — read and write values
  3. Release — garbage collector frees unreachable memory

Reachability

An object is kept in memory if it is reachable from a root (global object, current call stack, closures):

  let user = { name: 'Alice' };
user = null; // previous object may be collected if nothing else references it
  

Garbage Collection Algorithms

Modern engines (V8, SpiderMonkey) use generational GC:

  • Young generation — new objects, frequent fast collections
  • Old generation — long-lived objects, less frequent full collections

Mark-and-sweep: mark all reachable objects from roots, sweep unreachable ones.

Common Memory Leaks

1. Global variables

  function leak() {
    accidentalGlobal = 'oops'; // becomes window.accidentalGlobal
}
  

Fix: "use strict" or always declare with let/const.

2. Forgotten timers

  const id = setInterval(() => {
    // holds reference to large data
}, 1000);
// Fix: clearInterval(id) when done
  

3. Detached DOM nodes

  let element = document.querySelector('#big-list');
document.body.removeChild(element);
// element still referenced in JS — DOM tree kept in memory
element = null; // allow GC
  

4. Closures holding large data

  function createHandler() {
    const hugeData = new Array(1e6).fill('x');
    return function() {
        console.log(hugeData.length); // hugeData never collected
    };
}
  

Fix: only close over what you need:

  function createHandler() {
    const hugeData = new Array(1e6).fill('x');
    const size = hugeData.length;
    return function() {
        console.log(size);
    };
}
  

5. Event listeners not removed

Always remove listeners when components are destroyed (SPA route changes, modal close).

WeakMap and WeakSet

Allow GC of keys when no other references exist:

  const privateData = new WeakMap();

class User {
    constructor(name) {
        privateData.set(this, { name });
    }
    getName() {
        return privateData.get(this).name;
    }
}
// When User instance is unreachable, WeakMap entry is collected
  

FinalizationRegistry (Advanced)

Run cleanup when object is garbage-collected:

  const registry = new FinalizationRegistry((heldValue) => {
    console.log('Cleaned up:', heldValue);
});

let obj = { id: 1 };
registry.register(obj, 'resource-1');
obj = null; // eventually triggers cleanup callback
  

Monitoring Memory

Chrome DevTools → Memory tab:

  • Heap snapshot — see what objects exist
  • Allocation timeline — track allocations over time

Node.js:

  console.log(process.memoryUsage());
// { rss, heapTotal, heapUsed, external, arrayBuffers }
  

Best Practices

  • Null out references when done with large objects
  • Use WeakMap/WeakSet for caches keyed by objects
  • Clear intervals/timeouts and remove event listeners
  • Avoid storing DOM references longer than needed
  • Profile memory in long-running apps (SPAs, Node servers)

Understanding memory helps you build applications that stay fast and stable over time.