Recap

JavaScript Memory and Garbage Collection

Every lesson in a few lines, with the answer you'd give in an interview. About a 6-minute read.
Go to the course
Lesson 01

Stack vs heap

  • The stack holds function calls and their local variables, and is freed as soon as a function returns. The garbage collector only manages the heap.
  • Objects, arrays, strings and closures live on the heap. A variable holds a reference to them, not the data itself.
  • A closure keeps the variables it uses alive on the heap after its function returns.
In an interview
Where do JavaScript values live in memory?
Function calls and their local variables live on the stack, which is freed automatically when a function returns. Objects, arrays, strings and closures live on the heap, and that's the part the garbage collector manages. Small integers can be stored directly, but a variable holding an object only holds a reference to it.
Lesson 02

References and the object graph

  • Assigning an object to another variable copies the reference, not the object.
  • One remaining path from a root is enough to keep an object alive.
  • An unreachable object is only eligible for collection. It's freed the next time the collector runs, and you don't control when.
In an interview
When does an object become garbage?
When nothing reachable points to it any more. Setting a variable to null only removes one reference: if anything else still points to the object, it stays alive. And garbage isn't freed straight away. Its memory is reclaimed the next time the collector runs, which the engine decides.
Lesson 03

GC roots and reachability

  • Roots are global variables, the local variables of running functions, and what the browser holds, like DOM nodes and pending timers.
  • The collector's only test is whether a root can reach an object, not whether your code still needs it.
  • Objects that point to each other are still collected when no root reaches them.
In an interview
How does the garbage collector decide what to free? What about circular references?
It starts at the roots (global variables, the locals of running functions, and what the browser holds, like DOM nodes and timers) and marks everything it can reach. Whatever it can't reach is garbage. Circular references aren't a problem: two objects pointing at each other are still unreachable if no root leads to them. Reference counting couldn't free them, which is why engines trace from the roots instead.
Lesson 04

Young generation and minor GC

  • Most objects die young, so V8 keeps new objects in a small young generation and collects it often.
  • Allocating memory for a new object just moves a pointer forward, so allocation is very cheap.
  • A minor GC copies only the survivors, so dead objects cost almost nothing. Survive two and an object is promoted to the old generation.
In an interview
What is generational garbage collection, and why is it fast?
Most objects die young, so V8 puts new objects in a small young generation and collects it often. A minor GC, the Scavenger, copies only the objects that are still reachable into a fresh space, so its cost depends on what survives, not on how much was allocated. Objects that survive two minor GCs are promoted to the old generation.
Lesson 05

Major GC: mark, sweep, compact

  • The old generation is split into pages, and V8 tracks their free spots in a free list.
  • Mark walks from the roots; sweep frees what wasn't marked, without moving anything.
  • Free space left in small holes is fragmentation, and it can make the heap grow. Compaction moves live objects out of the worst pages and rewrites every pointer to them.
In an interview
How does V8 collect the old generation?
In three steps. Mark walks from the roots and marks everything reachable. Sweep frees whatever wasn't marked and adds the space to a free list. Over time that leaves holes too small to use, so compaction moves the live objects out of the most fragmented pages and updates every pointer to them. Moving objects is expensive, so V8 only compacts the pages that need it.
Lesson 06

How GC avoids freezing the page

  • At 60 frames per second a frame has about 16 ms, and GC work competes for it.
  • Incremental splits the work into small slices, concurrent runs it on helper threads, and parallel shares one pause across threads.
  • V8 uses all three. Compaction still needs a short pause.
In an interview
Does garbage collection block the main thread?
Partly. A stop-the-world pause grows with the heap and can drop frames, since a frame at 60 fps has about 16 ms. So V8 avoids it: minor GCs pause briefly but split the work across threads, old-generation marking runs incrementally and concurrently on helper threads, and sweeping happens in the background. Compaction is the part that still needs a short pause.
Lesson 07

How memory leaks happen

  • A leak is something still reachable that your code no longer needs. The collector can't tell the difference.
  • The common shapes: listeners with no cleanup, values left on window, timers nobody clears, detached DOM nodes still referenced, and caches that never evict.
  • The fix is always to break the reference. In React, return a cleanup function from useEffect.
In an interview
What causes memory leaks in JavaScript?
The garbage collector only checks whether an object is reachable, not whether you still need it. So a leak is a reference you forgot to remove. The usual suspects are event listeners that never unsubscribe, timers that are never cleared, values left on window, detached DOM nodes your code still holds, and caches with no size limit or expiry. The fix is to break the reference: unsubscribe, clear the timer, delete the entry, or cap the cache.
Lesson 08

Weak references and WeakMap

  • A Map key keeps its object alive. A WeakMap key doesn't.
  • A WeakMap entry lives exactly as long as its key, so there's no cleanup to forget.
  • Keys must be objects or non-registered symbols, and you can't iterate a WeakMap or read its size.
In an interview
What is a WeakMap, and when would you use it?
A WeakMap holds its keys weakly: an entry doesn't keep its key alive, and when the key is collected the entry goes with it. It's the right tool for attaching data to objects you don't own, like DOM nodes, without having to remember to clean up. The trade-off is that keys must be objects or non-registered symbols, and you can't loop over it or read its size, because entries can disappear at any time.
Lesson 09

Finding leaks with heap snapshots

  • A leak is memory that keeps growing after each GC while you repeat the same action.
  • Compare two heap snapshots and look for a class whose # Delta matches your repeat count.
  • Retained size shows what a leak costs; the retainers show who's holding it.
In an interview
How would you debug a memory leak?
First confirm it: memory that keeps growing after each GC as you repeat an action. Then, in Chrome's Memory panel, take a heap snapshot, repeat the action a few times and take another. The Comparison view shows what grew. Retained size tells you what's worth fixing, and the retainers show the path back to your code. Break that reference, then run the same test to confirm the fix.