JS Sketchbooksee JavaScript think ✏️
← back to phase 5

5.7 — Garbage collection

Since lesson 0.4 you've been borrowing memory slots by the thousand — every object, every array, every kept-alive closure context. Nothing you've written ever gave one back. So why hasn't your laptop melted? Because JavaScript employs a janitor: the garbage collector, sweeping continuously, on one beautifully simple rule.

The rule is reachability: start from the roots — global variables, the currently running call stack — and walk every rope and arrow you can (4.6's references, 5.3's outer links). Everything reached: kept. Everything unreached: an island, swept.

That one rule explains why closures "magically" stay alive — and exactly how real programs leak memory anyway.

watch it happen
let user = { name: "Mia" };
user = null;

function makeCounter() {
  let count = 0;
  return () => {
    count = count + 1;
    return count;
  };
}

const tick = makeCounter();
console.log(tick());
console.log(tick());

Line 1: the object lives in the heap (4.6), and the global variable user holds an arrow to it. From the roots you can walk to it → reachable → kept. Every object you can still use is, by definition, reachable.

the rootsglobals · running stack{ name: "Mia" }user →reachable: a rope from a ROOT (a global, the running stack) leads to it→ the collector keeps it(console: nothing yet)
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

Engines implement reachability with mark-and-sweep: periodically mark everything walkable from the roots, then sweep the unmarked.

V8 runs this in generations: new objects are checked often — most die young; survivors graduate to a rarely-checked old space. It runs mostly in tiny pauses between your code. When exactly? The engine decides; no API asks it to run.

The practical posture: don't fear creating objects; fear holding them forever. Creation is cheap and short-lived objects are collected almost free. The dangerous pattern is the long-lived container that only ever grows — logs, caches, listener lists. In test suites this appears as runs that get slower over hours: something is keeping every page object reachable.

Fun fact: hotel housekeeping runs mark-and-sweep. They don't ask "is this towel garbage?" — they check whether it's still reachable from a guest (in an occupied room, held by someone). Checkout cuts the rope; everything unreachable gets swept. You never call housekeeping to collect a specific towel — you just stop holding it.

your turn

⌨️ privacy by reachability

Build a wallet whose balance is IMPOSSIBLE to touch from outside — not hidden by convention, but unreachable by the rules of the language. Then prove it.

requirements:

  • A function makeWallet() with a local let balance = 0, returning an OBJECT with two methods: add(amount) (adds to balance, returns nothing) and total() (returns balance). Both reach balance through their closure — never through this.
  • Make one wallet. Add 40, then 60. Print total().
  • Now the proof: print w.balance — it must be undefined, because balance is NOT a property. It lives in the kept-alive context, reachable only through the two ropes.

when you press RUN, the console must show exactly:

100
undefined

✏️ Quick check 1

After line 2 runs, can the collector reclaim the object from line 1? Type yes or no:

let box = { size: "XL" };
box = { size: "S" };

✏️ Quick check 2

Type exactly what the SECOND log prints:

function makeTick() {
  let n = 0;
  return () => {
    n = n + 1;
    return n;
  };
}
const t = makeTick();
console.log(t());
console.log(t());

✏️ Quick check 3

The collector keeps anything ___ from the roots. Type the word.

teach it back

🗣️ Now teach it back

Explain to a friend: how does JavaScript decide what memory to free, why do closure variables survive after their function returns, and how can a program with a garbage collector still leak memory?

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
One rule: REACHABILITY. Walkable from the roots (globals, running stack) → kept; unreachable islands → swept. You drop ropes; the janitor sweeps.
Closures survive by the same rule: the returned function’s rope keeps its maker’s context reachable — private state between calls (and true privacy: no property, no access).
Leaks = accidental reachability (growing globals, caches, forgotten listeners). Don’t fear creating objects; fear holding them forever.
next: 5.8 Error handling →