Garbage Collection and Manual Memory

3.Who Clears the Tables?

A

In this chapter

We'll learn the two ways memory gets cleaned up — by the programmer (manual memory management) or automatically (garbage collection) — explained as two kinds of restaurant, plus what pointers and references are, and why a garbage collector still can't fix every leak.

12–14 min

The Problem in Real Life

"JavaScript has garbage collection," Anna says. "It's supposed to clean up memory automatically. So how can it leak?"

John smiles. "Garbage collection is brilliant — but it has one rule, and if you understand the rule, you'll understand every leak you ever see in JavaScript, Java, Python or C#. Let's go to a restaurant."

J

The garbage collector only throws away what nobody can reach. If you keep holding on, it has to keep it.

John

Clearing Your Own Table vs. Staff Who Clear It for You

Someone has to clean up

Every piece of heap memory must eventually be given back — either by the programmer or by the language.

Manual cleanup is risky

Forget to clean up and memory leaks; clean up too early and the program reads garbage.

Automatic isn't magic

Automatic cleanup only removes data that nothing references anymore.

Garbage Collection, Manual Memory and Pointers

Heap memory has to be given back when it's no longer needed, or the program slowly fills up. There are two ways to do it.

  • Manual memory management — a restaurant where you clear your own table: in languages like C and C++, the programmer asks for heap memory (in C, with a function called malloc) and must give it back when finished (with free). It's like a restaurant where you must carry your own plates back. It's very fast and gives full control — but if you forget, the plates pile up (a leak), and if you clear a table while someone is still eating, they reach for food that's gone (a dangling reference — chapter 5).
  • Garbage collection — a restaurant with staff who clear tables: in languages like JavaScript, Java, Python, C# and Go, a helper called the garbage collector (GC) runs every now and then, walks around, and clears every table that nobody is sitting at anymore. You never call free. This removes whole classes of bugs, which is why most modern languages use it.
  • The garbage collector's one rule — reachability: how does the GC know a table is free? It starts from the things the program can definitely still use — global variables, and the variables on the current stack trays — and follows every reference from them, and every reference from those, and so on. Everything it can reach is in use and stays. Everything it can't reach is garbage and is thrown away. If something is still referenced from anywhere reachable, the GC must keep it, even if the program will never actually use it again.
Table — Manual memory vs. garbage collection
FeatureManual (clear your own table)Garbage collection (staff clear tables)
LanguagesC, C++JavaScript, Java, Python, C#, Go
Who frees memoryThe programmer (free, delete)The garbage collector, automatically
Speed and controlMaximumVery good, with small pauses
Common bugsLeaks, dangling pointers, freeing twiceLeaks from references kept too long
Where it's usedOperating systems, games, embedded devicesMost web, business and app software
Table — Pointer vs. reference
FeaturePointer (C, C++)Reference (JavaScript, Java, Python, C#)
What it holdsA raw memory addressA managed link to an object
Can do arithmetic on it?Yes (move to the next address)No
Can point somewhere invalid?YesNo — always to a real object (or null)
Tracked by a garbage collector?NoYes

How the garbage collector decides

Roots

global variables + variables on the stack

the GC starts here

Follow every reference

and every reference from those

the result

Reachable → keep

e.g. the timers map and every timer in it

Unreachable → throw away

e.g. a hold object nobody points to

Pointers and references — two kinds of address notes: a pointer (in C and C++) is a raw memory address stored in a variable — like a written street address that you can change, add to, or point at anything, even somewhere wrong. Powerful, but easy to misuse. A reference (in JavaScript, Java, Python, C#) is a safer, managed version: it always points to a real object, you can't do arithmetic on it, and the language and garbage collector keep track of it. Both answer the question "where is the data?" — the reference just has safety rails.

The cost of garbage collection: cleaning tables takes time. Every so often, the GC needs a short moment to do its walk, which can briefly pause or slow the program. Modern collectors are very clever about keeping these pauses tiny. And when the heap is almost full, the GC works harder and harder to find space — which is exactly what the seat-hold graph showed before each crash.

Some languages use other ideas: Rust, for example, has no garbage collector but still prevents most memory bugs, using strict rules about which variable "owns" each piece of data, checked before the program runs. You don't need the details — just know that memory management is a key design choice for every language.

Back to the leak: now Anna can say exactly why JavaScript's GC isn't helping. The timers map is created when the service starts and lives for the whole day — it's always reachable. Every timer stored in it is therefore reachable too. And each timer holds on to its little function, which holds on to the seatId. The GC sees all of them as "in use." They're not garbage to the GC — they're things the program is still pointing at. 300,000 holds a day, never removed from the map.

Key Takeaway

Heap memory must be given back either manually (C/C++: you free it yourself — fast but risky) or automatically by a garbage collector (JavaScript, Java, Python, C#, Go). The garbage collector removes only what can't be reached from the program's live variables. So in a garbage-collected language, a leak means something reachable is still holding references to data you no longer need.

Why This Matters

Almost every language you'll use at work has a garbage collector, so you'll rarely free memory by hand. But you'll still meet leaks — and they'll always be the same shape: something long-lived, like a cache, a list, a map or an event listener, quietly holding references it should have let go of. Understanding reachability is how you find them.

Anna knows why the GC can't help: the map is always reachable. But one thing still puzzles her — local variables disappear when a function ends, so why does the timer's little function still remember seatId hours later? That question is about how long data lives, and where it lives inside a process.

Next