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.
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 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.
⌨️ 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 locallet balance = 0, returning an OBJECT with two methods:add(amount)(adds to balance, returns nothing) andtotal()(returns balance). Both reach balance through their closure — never throughthis. - Make one wallet. Add
40, then60. Printtotal(). - Now the proof: print
w.balance— it must beundefined, 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:
✏️ 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.
🗣️ 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.