How Garbage Collectors Actually Work
You know garbage collection exists: you allocate objects, stop referencing them, and eventually the memory comes back without a free() call. Memory & Garbage Collection, Explained covers that mental model - reachability, a gentle pass at mark-and-sweep, why pauses happen. This guide starts where that one stops. It's the algorithm-level view: the two competing ideas GC is built from, why almost no production collector uses either one in its pure form, and what to actually do when a GC log says your service is spending too much time collecting.
This is language-agnostic on purpose. Whether you write Java, C#, Go, Python, or JavaScript, your runtime's collector is some combination of the ideas in these three phases. Rust is the interesting exception - no GC at all - and Phase 3 explains why that trade-off works.
The phases
- Reference Counting and Mark-Sweep, the Two Basic Ideas - the two foundational strategies: count references and free at zero, or trace reachability from roots and sweep the rest. What each gets right, and the cycle problem that reference counting can't solve alone.
- Generational and Concurrent Collectors - why "most objects die young" reshapes the whole design, and how tri-color marking lets a collector work while your program keeps running instead of freezing it.
- Reading and Tuning a Real GC - what a GC log actually tells you, the few settings worth touching, and why reducing allocations beats fighting the collector. Plus Rust's alternative: no collector, ownership enforced at compile time.
By the end, a GC log stops being noise, a -Xmx flag stops being folklore, and "why doesn't X language just use a GC" has a real answer instead of a vibe.