OS & Systems Fundamentals • Garbage Collection FundamentalsMedium⏱️ ~2 min
Generational and Region Based Garbage Collection
The weak generational hypothesis states that most objects die young, observed empirically across nearly all workloads. Generational collectors exploit this by dividing the heap into young and old spaces. Young space (often called Eden or nursery) holds newly allocated objects and is collected frequently (minor GC) using fast copying evacuation. Objects surviving multiple collections (typically 3 to 15 minor cycles depending on tuning) are promoted to old space, which is collected infrequently (major GC) because it contains mostly long lived data.
Minor GC typically pauses for 1 to 10 milliseconds and reclaims 90 to 99 percent of young objects. Major GC pauses can range from tens of milliseconds to multiple seconds on large heaps if done with stop the world (STW) compaction. To avoid scanning the entire old generation during minor GC, collectors maintain remembered sets or card tables: data structures tracking pointers from old objects into young space, so only relevant old regions are scanned.
Region based collectors like G1 (Garbage First) and ZGC divide the heap into fixed size regions (often 1 to 32 MB each). Instead of young versus old, the collector prioritizes regions with the most garbage first, bounding pause time by evacuating only a subset of regions per cycle. This allows tuning pause budgets (e.g., target max 10 ms pauses) independent of total heap size, making 100+ GB heaps practical for low latency services.
Netflix runs tens of thousands of Java Virtual Machines (JVMs) with heaps from 32 to 128+ GB using region based collectors, achieving p99 GC pauses in single digit milliseconds under steady load. Without region based collection, full heap compaction on 100 GB heaps could pause for seconds, violating service level objectives (SLOs) for streaming and personalization APIs handling tens of thousands of requests per second per instance.
💡 Key Takeaways
•Weak generational hypothesis: 90 to 99 percent of objects die young, so frequent young collections with fast evacuation reclaim most memory with minimal pause time (1 to 10 ms typical)
•Promotion and tenuring: objects surviving 3 to 15 minor GC cycles move to old space; tune threshold to balance promotion rate against young space sizing and collection frequency
•Remembered sets and card tables track old to young pointers so minor GC only scans relevant old regions, avoiding full heap traversal that would make young collections expensive on large heaps
•Region based collectors prioritize regions with most garbage and evacuate incrementally, achieving pause time targets (e.g., 10 ms max) independent of heap size, enabling 100+ GB heaps with predictable latency
•Trade off: generational collectors assume age predicts lifetime, which fails for workloads with uniform object lifetimes (e.g., caches with time to live TTL eviction), causing premature promotion and inflated old space occupancy
📌 Examples
Java web service allocates request scoped objects (parsed JSON, intermediate computations): 95% die before next minor GC (10 ms pause every 50 ms). Session objects surviving 8 minor GCs promoted to old space, major GC runs every 5 minutes with 50 ms pause
Netflix microservice with 64 GB heap and G1 collector: young collections every 100 ms with 2 to 5 ms pauses, old collections target 10 ms max by evacuating 4 to 8 regions per cycle, achieving p99 GC pause under 10 ms at 50K requests per second
Python cyclic GC divides objects into 3 generations: generation 0 collected most frequently (every 700 allocations by default), generation 2 (oldest) collected least often; Instagram disabled generation 2 GC during request handling to cut p95 latency by 10 percent