completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Why garbage collection?

Learn why JavaScript garbage collection uses roots, reachability, object graphs, pauses, and trade-offs to reclaim memory safely.

By the end, you can
  • 01
    Explain reachabilityStart at the roots, follow references, and say which objects are kept and which are garbage, even when objects point at each other in a loop.
  • 02
    Compare two ways to free memoryCompare freeing memory by hand, as in C, with JavaScript's garbage collection, and say what you still control in JavaScript.
  • 03
    Weigh pauses against memoryExplain how throughput, latency, spare memory, and stop-the-world pauses affect how smooth a page feels.

Why garbage collection exists

Your program makes objects all the time: plain objects, arrays, functions, and more. Each one takes up memory. While your code can still use an object, its memory must stay. When no code can use it any more, the memory can be given back and used again.

The hard part is knowing when that moment comes. In JavaScript, you don't do this yourself. The engine does it for you. The engine is the part of the browser, or of Node, that runs your code.

Definition

Garbage collection means JavaScript gives back unused memory by itself. The part of the engine that does it is the garbage collector, or GC for short. It starts from a few fixed starting points, called roots. From there, it follows every link from one object to the next. Objects it can reach are kept. Objects it can't reach are garbage, and their memory can be used again. A collector that follows links like this is called a tracing collector.

A few words come up again and again in this lesson:

  • A root is a starting point that JavaScript always treats as in use. Examples: global variables, the local variables of the function that is running now, and things the browser holds for you, such as a timer or an event listener.
  • A reference is a link to an object. When cart.items holds an array, cart has a reference to that array.
  • An object is reachable when you can start at a root and follow references until you get to it.
  • The object graph is the map of all objects and the references between them.

This lesson comes after Exceptions & unwinding. Earlier lessons show memory from the outside: memory management, stack and heap, WeakMap and WeakSet, WeakRef and finalization, and finding memory leaks. This lesson looks inside the engine. Heap layout is a companion lesson about how the engine splits up its memory. Later lessons cover the main ways collectors work: reference counting, mark-sweep, generational GC, write barriers, and incremental or concurrent GC.

Freeing memory by hand vs automatically

Some languages make you free memory by hand. In C, you ask for memory with malloc. When you are done, you give it back with free. This gives you full control. But every step must be exactly right. Three mistakes are common:

  • Using memory after you freed it. This is called "use after free".
  • Freeing the same memory twice. This is called "double free".
  • Forgetting to free memory. It is never given back. This is called a leak.

Each of these can crash the program or waste memory.

Freeing memory by hand in C, with two mistakesC
// This is C, not JavaScript. Here you free memory by hand.char *name = malloc(32);strcpy(name, "Asha");free(name);printf("%s", name);     // mistake: used after it was freedfree(name);             // mistake: freed a second time// If you forgot free(name), this memory would leak.

Line 2 asks for 32 bytes of memory. Line 4 gives them back. Line 5 still reads that memory after it was freed. Line 6 frees it a second time.

JavaScript has no free for objects. Instead, the garbage collector checks which objects can still be reached. Look at the code below:

A returned function keeps the receipt in memoryPop out in the code editor (opens in a new tab)JavaScript
function makeReceiptReader() {  const receipt = { customer: "Asha", total: 1200 };  return function readTotal() {    return receipt.total;  };} const readTotal = makeReceiptReader();console.log(readTotal());
  • Line 2 makes a receipt object inside makeReceiptReader.
  • Line 3 returns a function, readTotal, that still reads the receipt. A function that remembers variables from the place it was made is called a closure.
  • Line 8 saves that function in a variable, also called readTotal.
  • Line 9 calls it, and it prints 1200.

makeReceiptReader has finished, but the receipt is still needed. readTotal can still be reached, and it reads the receipt. So the receipt can be reached too, and JavaScript keeps it.

A box called readTotal holds the readTotal function. An arrow labelled reads goes to a cart called receipt, which holds total 1200. The function is still in use, so the receipt is kept.readTotal()readTotalstill in usereadstotal: 1200receiptkept
readTotal still reads the receipt. So JavaScript keeps the receipt in memory for as long as readTotal can be called.

The table compares the two ways. Each row is one way of handling memory.

Freeing memory by hand vs automatically
Way of handling memoryWho gives the memory back?What can go wrong?
Freeing by hand, as in CYour code asks for memory and must give it back itself with free.You might forget to free it (a leak), use it after freeing it, or free it twice.
Garbage collection, as in JavaScriptThe engine gives back the memory of objects that no root can reach.Those mistakes go away. But the collector takes time and needs extra memory.
Your job in JavaScriptKnow which code owns each piece of data. Remove listeners and timers you no longer need.You never call free, but a forgotten reference can still keep big data alive.

Automatic memory removes the mistakes of freeing by hand. But it still has a cost. The collector needs time to do its work. It also needs extra memory: some for its own notes, and some spare room. And your code still decides which references stay alive.

Roots and reachability

To decide what to keep, the collector asks one question about each object: can I get to it from a root by following references?

  • If yes, the object is kept.
  • If no, the object is garbage. This is true even when it points to other objects.

The code below acts out that question with plain arrays. Each name in graph lists the names it points to.

Find everything a root can reachPop out in the code editor (opens in a new tab)JavaScript
const graph = {  root: ["cart"],  cart: ["items"],  items: [],  detachedA: ["detachedB"],  detachedB: ["detachedA"],}; function markFrom(roots) {  const marked = new Set();  const worklist = [...roots];  while (worklist.length > 0) {    const node = worklist.pop();    if (marked.has(node)) continue;    marked.add(node);    for (const child of graph[node]) worklist.push(child);  }  return [...marked].sort();} console.log(markFrom(["root"]).join(","));
  • Line 1 makes the graph. root points to cart, and cart points to items. detachedA and detachedB point only at each other. Objects that point at each other in a loop form a cycle.
  • Line 9 starts markFrom. To mark an object means to put it on the "keep" list.
  • Line 10 makes that list as a Set, a collection that holds each value only once. Line 11 makes a worklist: the names still waiting to be visited.
  • Line 14 skips a name that is already marked. So a cycle can't keep the loop going forever.
  • Line 15 marks one object. Line 16 adds the names it points to onto the worklist.
  • Line 21 prints the marked names in alphabetical order.

It prints cart,items,root. detachedA and detachedB are not printed, because no path from root leads to them. They point at each other, but that doesn't help. A tracing collector only cares about paths from a root.

Left: a root tag points to cart, and cart points to items, so both are reachable and kept. Right: detachedA and detachedB point at each other, but no root points to them, so they are garbage.rootcartitemsreachable, so keptno root points heredetachedAdetachedBnot reachable, so garbage
Follow the arrows from root: they reach cart and items. The two detached objects point at each other, but no arrow from a root reaches them, so they are garbage.

The step-through below runs a bigger version with two roots. One is globalThis, the global object that holds global variables. The other is a timer that the browser holds for you. Press Step to watch the marking, one line at a time.

Step through marking from the roots
Step 0 of 8Ready
Your turn: follow the blue line

Step through marking from the roots. This is a small model, not the browser's real collector, but it makes the same choice: keep what the roots can reach.

Running in
  1. script
Next: line 1
  globalThis: ["cart"],  cart: ["items"],  items: [],  detachedA: ["detachedB"],  detachedB: ["detachedA"],  timer: ["callback"],  callback: ["cache"],  cache: [],};const roots = ["globalThis", "timer"]; function markFromRoots(names) {  const marked = new Set();  const worklist = [...names];  while (worklist.length > 0) {    const name = worklist.pop();    if (marked.has(name)) continue;    marked.add(name);    for (const child of graph[name]) worklist.push(child);  }  return [...marked].sort();} console.log(markFromRoots(roots).join(","));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Real-life analogyBalloons on strings

At a party, a balloon stays while its string leads back to someone's hand. Some balloons are tied to other balloons. They stay too, as long as the chain of strings ends at a hand. Two balloons tied only to each other float away, because no hand holds them.

In real life: A hand holds a balloon's string
In JavaScript: A root is where the search starts
In real life: Strings tie balloons to other balloons
In JavaScript: References link one object to the next
In real life: Two balloons tied only to each other
In JavaScript: A cycle that no root reaches is garbage
In real life: A balloon whose strings lead to a hand stays
In JavaScript: Objects a root can reach are kept

The object graph: one map of every object

Real apps are bigger than this small example. In one app, all of these can happen at once:

  • A root points to a cart, and the cart points to its items.
  • Two objects point only at each other.
  • An event listener keeps a closure, and the closure keeps a big draft order.

The collector doesn't see these as separate stories. It sees one big map of objects and references: the object graph. In the playground below, each button adds or removes one reference. Press one, then check which objects are reachable and which are garbage.

Playground: add or remove links between objects
The links the buttons changeJavaScript
const roots = ["window", "event-listener"]; window.cart = cart;              // button: the root points to cartcart.items = items;              // button: cart points to itemsdetachedA.friend = detachedB;    // A points to BdetachedB.friend = detachedA;    // B points to Abutton.onclick = () => draftOrder.total; // button: the listener keeps the draft order const result = markFromRoots(roots, graph);console.log(result.reachable, result.garbage);
What is kept2 garbage
rootswindow, event-listener

The search starts here.

reachablecart, draft-order, event-listener, items, listener-closure, window

These objects are kept.

garbagedetached-a, detached-b

No path from a root reaches these objects.

visit orderevent-listener → listener-closure → draft-order → window → cart → items

A real engine may visit them in another order. Only the final lists matter.

windowroot

root: the global object

Points to: cart

cartreachable

a shopping cart object

Points to: items

itemsreachable

an array of items

Points to: nothing

detached-agarbage

A, which points only to B

Points to: detached-b

detached-bgarbage

B, which points only to A

Points to: detached-a

event-listenerroot

root: an event listener the browser holds

Points to: listener-closure

listener-closurereachable

the closure of the click function

Points to: draft-order

draft-orderreachable

a big draft order

Points to: nothing

Try it yourself

The model marks 6 objects as reachable and leaves 2 as garbage. A and B, which point at each other, are garbage, because no root reaches them.

This is a small model. It doesn't look inside your browser's real memory. It runs this lesson's marking function on a small graph.
Real-life analogyA WhatsApp group

Start with one person in a WhatsApp group. Look at their saved contacts, then at those people's contacts, and so on. Everyone you find this way can be reached. Two people who only have each other's numbers can't be found this way.

In real life: You start with one person in the group
In JavaScript: The search starts at a root
In real life: Each person shows you their saved contacts
In JavaScript: Each reachable object leads to the objects it points to
In real life: Two people who only know each other
In JavaScript: A cycle that no root reaches is garbage
In real life: One saved contact leads to many more people
In JavaScript: One closure or listener can keep many objects alive

This is why finding a leak starts with the path, not with the size. In JavaScript, a leak is memory that stays reachable longer than you meant. A big array only stays because some path from a root still reaches it. So look for the first link on that path that shouldn't be there, such as a forgotten listener, timer, or global variable.

Proof in Node: an object with only a weak link goes away

You can't make the browser collect garbage when you want. Your app code should never depend on when a collection happens. But for a test, Node can help. Node runs JavaScript with V8, the same engine Chrome uses. If you start Node with the --expose-gc flag, it gives you a gc() function. Calling it asks V8 to collect garbage now.

The code also uses a WeakRef. A WeakRef is a weak reference: it lets you find an object, but it doesn't keep the object alive. Its deref() method gives back the object, or undefined once the object has been collected.

Show that a weak-only object is collected (Node only)JavaScript
const wait = () => new Promise((resolve) => setTimeout(resolve, 0)); function weakOnly() {  let target = { label: "weak-only", data: new Array(1000).fill("x") };  const weak = new WeakRef(target);  target = null;  return weak;} const rooted = { label: "rooted", data: new Array(1000).fill("y") };const rootedRef = new WeakRef(rooted);const weakRef = weakOnly();let collected = false; for (let attempt = 0; attempt < 8; attempt += 1) {  await wait();  globalThis.gc();  await wait();  if (weakRef.deref() === undefined) {    collected = true;    break;  }} console.log(  `Node ${process.versions.node} (V8 ${process.versions.v8.split(".").slice(0, 2).join(".")})`,);console.log("weak collected", collected);console.log("root survives", rootedRef.deref()?.label);
  • Lines 3 to 8: weakOnly makes an object on line 4 and wraps it in a WeakRef. Then it sets target to null. After that, only the weak reference points at the object.
  • Line 10 makes rooted, a top-level const, so a root keeps it. Line 11 wraps it in a WeakRef too, only so we can check it later.
  • Lines 15 to 23 try up to 8 times. Each time, they wait (line 16), call gc() (line 17), wait again (line 18), and check whether the weak object is gone (line 19).
  • Lines 25 to 29 print the Node and V8 versions, whether the weak object was collected, and the label of the rooted object.

Why wait? JavaScript keeps the object of a new WeakRef alive until the code that is running now finishes. Each wait() uses setTimeout to let the current code finish and carry on a moment later. Only then can gc() clear the weak-only object.

What the lesson test checks

The lesson test runs this code in a separate Node 22 process, which uses V8 12.4. It checks two things. After a few gc() tries, the weak-only object is gone. And the object in rooted is still there, so the last line prints rooted.

Treat this as proof of the reachability rule, not as a way to write apps. For everyday uses of weak references, see the WeakRef and finalization lesson.

What the collector must balance

A collector has two jobs, and they pull against each other. It must find all the garbage correctly. And it must do that without making the program feel slow or use too much memory. Engine teams pick their methods to balance three things:

  • Throughput: how much of the total time goes to your program's real work, instead of to collecting.
  • Latency: how long one single pause lasts. While the collector pauses your code, the page can't respond.
  • Memory overhead: how much extra memory the collector needs. This includes spare room, the collector's own notes, and garbage it hasn't cleared yet.

The next lessons show the collector's methods one by one. This lesson only names the three things they balance.

These are teaching models

The step-throughs and playgrounds on this page run real JavaScript functions from this lesson. But they are small models. They don't look inside your browser's real memory.

Throughput, latency, and memory: you can't win all three

The same program can be collected in different ways. Each way gives something up:

  • Collect rarely, in big batches. Your code runs longer between collections. But when a collection comes, the pause is long.
  • Collect often, in small batches. Each pause is short. But all the pauses together may take more time.
  • Leave more spare memory. Collections happen less often. But the most memory in use at one time, called the peak, goes up.

Each row of the table is one thing a collector balances. The last column says what engines usually do about it.

Four things a collector balances
What it is aboutWhat question does it ask?What do engines usually do?
ThroughputHow much of the total time goes to real work instead of collecting?Collect in bigger batches, so less time goes to collecting in total.
LatencyHow long does one single pause last?Keep pauses short, because typing, scrolling, and animation need quick answers.
Memory overheadHow much extra memory does the collector need, such as spare room, its own notes, or garbage not cleared yet?Leave more spare room. Collections happen less often, but the peak memory goes up.
When pauses happenDoes the work happen all at once, in small pieces, alongside your code, or when the page is idle?Any of these is allowed. Your code must behave the same either way.

The playground below makes new objects in 10 bursts, and each burst adds a few MB of memory. All three plans get the same bursts. Pick a plan. Then compare its longest pause, its total pause time, and its peak memory.

Playground: pick a plan for collecting
Add memory in bursts and collect at a limitJavaScript
const bursts = [10, 12, 9, 14, 8, 11, 13, 7, 12, 10]; function simulate(strategy) {  let heap = 20;  let totalPause = 0;  for (const allocated of bursts) {    heap += allocated;    if (heap >= strategy.threshold) {      totalPause += strategy.pause(heap);      heap = strategy.reclaimTo;    }  }  return totalPause;} console.log(simulate(frequentSmall));
Result in the modelBalanced collections
Rare big collections22 ms longest

1 collection · 22 ms of pauses · 84 MB peak memory

Balanced collections13.9 ms longest

2 collections · 27.1 ms of pauses · 65 MB peak memory

Frequent small collections7.8 ms longest

5 collections · 37.7 ms of pauses · 43 MB peak memory

Burst 413.9 ms pause

Memory 65 MB → 22 MB after collecting.

Burst 813.2 ms pause

Memory 61 MB → 22 MB after collecting.

Step 2 of 6Balanced collections

Balanced collections: 2 collections, 27.1 ms of pauses in total, 13.9 ms for the longest pause, and 65 MB of memory at the peak, in this model.

This is a small model, not a measurement of your browser. Real engines mix several methods to keep pauses short.
Top: rare big collections make one pause of 22 ms and reach 84 MB at most. Bottom: frequent small collections make five pauses of 7.6, 7.8, 7.3, 7.4 and 7.6 ms and reach 43 MB at most.Rare big collections22 ms1 pausepeak 84 MBFrequent small collections7.6 ms7.8 ms7.3 ms7.4 ms7.6 ms5 pausespeak 43 MB
The same work, collected two ways. Rare big collections pause once, for 22 ms. Frequent small ones pause five times, each under 8 ms, and use less memory, but their pauses add up to more time (37.7 ms).

Real engines mix several methods. Later lessons cover generational collection, write barriers, and incremental or concurrent marking. For now, remember three short meanings: throughput is the total cost, latency is the length of one pause, and memory overhead is the extra memory.

Stop-the-world pauses

A stop-the-world pause is a moment when JavaScript stops completely so the collector can work. The collector needs this for some of its work, because objects must not change while it looks at them. During the pause, the page can't run your click handler or your animation code.

This matters for smooth animation. Most screens show 60 pictures per second. Each picture is called a frame. So each frame has about 16.7 ms (1000 ms divided by 60). In that time, the browser must run your JavaScript and also draw the page. If a frame takes longer, the frame is missed and the animation stutters.

The step-through below draws three frames. Each one does 5 ms of work and adds 4 MB of new objects. When memory reaches 24 MB, the model pauses for a collection.

Step through a pause inside one frame
Step 0 of 14Ready
Your turn: follow the blue line

Step through three frames. You'll see why the length of one pause matters, even when the total time spent collecting is small.

Running in
  1. script
Next: line 1
let heapMB = 12; function renderFrame(workMs, allocatedMB) {  heapMB += allocatedMB;  if (heapMB >= 24) {    const pauseMs = 12;    heapMB = 10;    return workMs + pauseMs > frameBudgetMs ? "missed frame" : "smooth";  }  return workMs <= frameBudgetMs ? "smooth" : "missed frame";} console.log(renderFrame(5, 4));console.log(renderFrame(5, 4));console.log(renderFrame(5, 4));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The third frame does only 5 ms of normal work. But a 12 ms pause pushes it to 17 ms. That is over the 16.7 ms budget, so the frame is missed. If frames are new to you, read rendering performance too.

Three frames and a dashed line at 16.7 ms. Frames 1 and 2 each do 5 ms of work and stay smooth. Frame 3 does 5 ms of work plus a 12 ms pause, which goes past the line, so it is missed.16.7 msframe 1work 5 mssmoothframe 2work 5 mssmoothframe 3work 5 mspause 12 msmissed
Each frame has 16.7 ms. Frames 1 and 2 only do 5 ms of work. In frame 3, the 12 ms pause pushes the total to 17 ms, past the line.

In Node, you can watch real V8 collections happen. The --trace-gc flag makes Node print one line for each collection.

Watch V8 collect garbage (Node only)JavaScript
const keep = [];for (let index = 0; index < 20000; index += 1) {  keep.push({ index, data: new Array(20).fill(index) });}globalThis.gc();console.log("kept objects", keep.length);

Line 1 makes an array that keeps every new object reachable. Lines 2 to 4 add 20,000 small objects to it. Line 5 calls gc(). Line 6 prints how many objects were kept.

What the lesson test checks

The lesson test runs Node with --trace-gc --expose-gc. With V8 12.4, the printed lines name each kind of collection, such as Scavenge (a quick clean-up of new objects) and Mark-Compact or Mark-sweep (a full clean-up). They also show how many milliseconds each pause took. The test doesn't check exact memory addresses, sizes, or times, because they change from run to run.

What you do in everyday code

Most days, you don't change how the collector works. Instead, you control which references stay alive. When a part of your page goes away, also remove the things that still point at its data. These habits help:

  • Measure first. Use heap snapshots and allocation timelines before changing code. These DevTools tools show what is in memory and when it was made.
  • Remove event listeners, observers, and timers you no longer need. Each one can keep a closure, and its data, reachable.
  • Give caches a size limit. Write down which part of the code owns each list or map that lives a long time.
  • Keep big temporary objects inside a function. When the function returns, its local variables stop being roots.
  • Check how slow an interaction really is before you blame the collector.
  • For the details of each tool, such as WeakRef, use the everyday lessons linked at the top.

Garbage collection is a safety net. It doesn't decide which part of the code owns what. The collector frees memory that no root can reach. It can't tell whether your global cache still matters.

Root, reachable, or garbage?
  • The global object, globalThis or window, where the search starts.
  • The cart local variable in the function that is running now.
  • An items array that a reachable cart object points to.
  • Data used by a function that a running timer still holds.
  • Two objects that only point at each other, with nothing else pointing at them.
  • A cache object, after the only event listener that kept it was removed.
Try it yourself
0 of 6 correct

Sort each card: is it a root, a reachable object, or garbage? Read each explanation, even when you get it right.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

  • “Garbage means useless.” Garbage means no root can reach it. An object can be reachable and still useless. That is a leak.
  • “Cycles always leak.” Two objects that only point at each other, with no path from a root, are garbage. A cycle leaks only when a root still reaches it.
  • “Memory is freed the moment a function returns.” When a function returns, its local objects only become ready to be collected, if nothing else points at them. When the collection actually runs is up to the engine.
  • “WeakRef is a clean-up tool for normal app code.” It is for rare, advanced cases. Don't depend on when an object gets collected.
  • “GC pauses and total GC time are the same thing.” One long pause makes the page feel slow, even when the total time spent collecting is small.

These words are easy to mix up. Each row says what a word means and what it does not mean.

Garbage collection words that are easy to mix up
WordWhat does it mean?What does it not mean?
UnreachableNo path of references leads from any root to the object.Useless. The collector can't tell what you still need; it only follows references.
ReachableAt least one path of references leads from a root to the object.Sure to be used again. A reachable object can still be a leak.
RootA starting point JavaScript always treats as in use, such as a global, a local variable of a running function, or a timer the browser holds.Every object that has a name in your code.
LeakMemory that stays reachable longer than you meant.A mistake inside the collector. Usually your own code still points at the data.

Practice exercises

Exercise 1 · Warm-upPredict the marked names

Read the code. Type what it prints, with commas between the names.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const graph = { root: ["cart"], cart: ["items"], items: [], a: ["b"], b: ["a"] };
const marked = new Set();
const worklist = ["root"];
while (worklist.length) {
  const node = worklist.pop();
  if (marked.has(node)) continue;
  marked.add(node);
  for (const child of graph[node]) worklist.push(child);
}
console.log([...marked].sort().join(","));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upRead a WeakMap while its key is still in use

    A WeakMap is a map whose keys are objects. It doesn't keep those keys alive. What does this code print while strong still points at the key?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const strong = { name: "rooted" };
    const weak = new WeakMap();
    weak.set(strong, "metadata");
    console.log(weak.get(strong));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeStop a mark function from visiting twice

      Name the kind of collection that stops this function from visiting the same object again.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function mark(node) {
        if (/* already visited? */) return;
        // mark node, then visit children
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind what keeps the draft order alive

        Which reference should you remove so draftOrder can be collected?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function mount(button) {
          const draftOrder = { lines: new Array(10000).fill("item") };
          button.addEventListener("click", () => console.log(draftOrder.lines.length));
        }
        // This part of the page is removed, but the button keeps its listener.

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengePick a plan for smooth animation

          An animation stutters during collections. Which plan from the playground would you try first?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const workMs = 5;
          const pauseMs = 12;
          console.log(workMs + pauseMs > 16.7 ? "missed frame" : "smooth");

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeApply it to a real site

            Your checkout page uses more and more memory during a long visit. What should you do before you rewrite the cart code?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, find the roots and follow the references. Keep throughput, latency, and memory apart.

              Garbage collection basics quiz · 7 questionsScore: first tries count
              1. Question 1 of 7Where does a tracing garbage collector start its search?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this cart code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const root = { name: "root" };
                root.cart = { items: ["tea"] };
                console.log(root.cart.items.length);

                Choose an answer to see the explanation.

              3. Question 3 of 7Two objects point at each other, but no root points at either one. What are they?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does this frame code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const workMs = 5;
                const pauseMs = 12;
                console.log(workMs + pauseMs > 16.7 ? "missed frame" : "smooth");

                Choose an answer to see the explanation.

              5. Question 5 of 7Which sentence best describes how JavaScript frees memory?

                Choose an answer to see the explanation.

              6. Question 6 of 7In the model, which choice makes each pause shorter?

                Choose an answer to see the explanation.

              7. Question 7 of 7What should you usually do with this knowledge in everyday code?

                Choose an answer to see the explanation.

              Key takeaways

              • Garbage collection frees objects that your program can no longer reach from a root.
              • Roots include global variables, the local variables of running functions, and things the browser holds for you, such as timers and listeners.
              • Following references from the roots decides what is reachable. Objects that only point at each other, with no path from a root, are still garbage.
              • Collectors balance throughput, latency, and memory. The length of one pause and the total time spent collecting are different things.
              • A stop-the-world pause stops JavaScript. It matters when each frame has only about 16.7 ms.
              • Your job: remove links that keep memory reachable by mistake, and measure before you try to make things faster.

              Remember the one-liner.
              Garbage is not “old” memory. Garbage is memory that no root can reach.

              Coming next: Reference counting & cycles. Reference counting keeps a count of how many references point at each object. That lesson shows why cycles make counting hard. Later lessons cover mark-sweep, generational collection, write barriers, and incremental or concurrent GC.

              CompleteFrontend Clear concepts. Working examples.