Garbage collection.
A program creates objects on the heap. Some it stops using. The GC\'s job is to figure out which ones are no longer reachable from anywhere live, and reclaim their memory — without the program noticing. Watch tri-color mark-and-sweep walk a small object graph, handle a cycle, and free the unreachable bits.
Eight objects on the heap. Only one is rooted (User, A) — meaning it's reachable from a stack variable, a global, or a register. The rest are connected to each other or floating. The GC's job: find everything reachable from roots; reclaim the rest. We'll walk a classic mark-and-sweep, then mention how generational and concurrent collectors improve on it.
- GC root
- A reference from outside the heap that's definitely live: stack locals, global variables, CPU registers, thread-local storage. Reachability starts here.
- Reachable
- Reachable from a root by following references transitively. Everything else is garbage.
- Tri-color marking
- White = unvisited, gray = visited but children unscanned, black = visited and children scanned. The mark phase moves objects from white → gray → black.
Why ref-counting alone isn\'t enough
Reference counting increments a counter on every assignment, decrements on every "I\'m done." When the count hits 0, free immediately. Simple, predictable. Until you have a cycle: A points to B, B points to A. Neither count ever reaches 0, even when nothing else references either of them. Pure ref-counting languages (Swift, early Python) need cycle detection as a backup, or you have to manually break cycles with weak references.
Generational GC · the killer optimisation
The generational hypothesis: most objects die young. A web request creates short-lived objects; the response gets serialized and they all become garbage in milliseconds. Generational collectors split the heap into a small "young" generation and a larger "old" generation. They collect young aggressively (cheap, finds most garbage) and old rarely (expensive, but most stuff there is long-lived). JVM\'s G1 and ZGC, V8\'s scavenger + Major GC, .NET\'s tiered GC — all use this principle.
Concurrent vs stop-the-world
Old GCs paused the application entirely while collecting. Multi-second pauses on big heaps. Modern GCs (Go\'s GC, JVM\'s ZGC, V8\'s incremental marker) do most of the work concurrently with the application. They still need short stop-the-world phases for safety (e.g. marking roots), but those are measured in single-digit milliseconds even on 100 GB heaps. The catch: concurrent GCs need write barriers — every pointer write costs a few extra instructions to inform the GC. The cost is a few percent of CPU; the payoff is no multi-second pauses.
GC internals · the long version →
Go\'s tricolor concurrent GC, JVM\'s G1 / ZGC / Shenandoah, V8\'s scavenger + Major GC, .NET\'s tiered GC, write barriers in detail.
Open the Codex →