Garbage collection: how JavaScript frees memory you no longer use
Reachability, mark-and-sweep, V8's young and old generations, concurrent marking in Orinoco, and why GC pauses cause jank. Explained with a runnable demo.
Comes up in: JavaScript, Web fundamentals
Learn#
Sameer’s single-page app gets slower every hour. He set modal = null and expected the memory to come back, but a listener still pointed at the modal.
JavaScript has garbage collection: the engine frees memory for you. It keeps every object it can reach and frees the rest. Reachability means “can be found by following references from a root”, such as a global variable or a running function’s locals.
import { setFlagsFromString } from "node:v8";
import { runInNewContext } from "node:vm";
setFlagsFromString("--expose-gc");
const gc = runInNewContext("gc"); // lets this demo force a collection
let cart = { items: new Array(1_000_000).fill(0) };
const kept = { cart }; // a second reference
const ref = new WeakRef(cart); // a reference that does not keep it alive
cart = null;
await new Promise((r) => setTimeout(r, 0));
gc();
console.log("still reachable:", ref.deref() !== undefined);
kept.cart = null;
await new Promise((r) => setTimeout(r, 0));
gc();
console.log("after removing the last reference:", ref.deref() !== undefined);Output (verified)
still reachable: true after removing the last reference: false
Setting one variable to null freed nothing while another reference remained.
Memory
- root: window
- root: current function's locals
Roots are where marking starts: global variables and the variables of functions still running.
Read the steps as text
- Roots are where marking starts: global variables and the variables of functions still running.
- Mark: follow every reference from the roots. app, user and cart are marked.
- oldModal and its listener point at each other, but nothing marked points at them.
- Sweep: every unmarked object is freed, including the cycle.
Predict#
Predict first
Two objects reference each other, and nothing else references either. Are they freed?
Mark-and-sweep starts from the roots. A cycle that no root reaches is never marked, so it is freed. The tempting answer comes from reference counting, an older technique that does leak cycles.
Interview angle#
Say it like this — about 30 seconds
“JavaScript frees objects that are no longer reachable from roots, such as globals and the current call stack. V8 marks everything reachable and sweeps the rest, so cycles are not a problem. It is generational: most objects die young, so new objects go in a small young space that is cleaned often and cheaply. Survivors move to the old space, which is marked mostly concurrently, off the main thread. Leaks happen when something reachable still points at data you no longer need.”
Follow-up Why are short-lived objects cheap?
Follow-up Can you force garbage collection in the browser?
--expose-gc flag for tests.Follow-up Does setting a variable to null free memory?
Practice#
Guided
Write a tiny mark-and-sweep collector over a heap described as data.
Done when
- Everything reachable from a root is kept, however deep.
- An unreachable cycle is freed, and cycles never cause an endless loop.
- Both lists are sorted, and every id appears in exactly one of them.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Which objects does the garbage collector free?
Objects not reachable from any root.
Does mark-and-sweep leak reference cycles?
No. Unreachable cycles are never marked, so they are freed.
What is the generational hypothesis?
Most objects die young.
Why do GC pauses matter in the browser?
Main-thread pauses delay input and frames, which users see as jank.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Roots include globals, the variables of running functions, and things the engine holds, such as active timers and DOM listeners. Anything reachable from them stays alive Source: MDN: Memory management.
Check yourself: Name two kinds of GC roots.
Global variables and local variables of functions on the call stack.
How it works underneathSenior rounds
Check yourself: What does the mark phase do?
Visits every object reachable from the roots and marks it as live.
Edge cases and trapsWhere people slip
Check yourself: In V8, where do new objects go first?
The young generation, cleaned by the scavenger.
In productionStaff-level answers
Check yourself: Which DevTools panel shows GC pauses on a page?
The Performance panel (look for GC events on the main thread).
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- How does garbage collection work in JavaScript?