Java 34: Heap, Stack & Generational Memory
easy⏱ 5 mincoursejava
Stack vs heap
Stack: per-thread, fixed-size (default 1MB), holds frames — local variables, operand stack, return address. Heap: shared across threads, holds all objects and their fields. Stack allocation is free; heap allocation requires the GC to track and eventually collect.
Young generation: Eden + Survivors
New objects land in Eden. When Eden fills, a minor GC copies survivors to a Survivor space (S0 or S1). Next minor GC copies from the active survivor + Eden to the other survivor. Each copy increments the object's 'age'. When age exceeds a threshold (default 15), the object is promoted to Old.
Old generation
Long-lived objects (caches, singletons, old-generation data structures). Collected by a major GC — much more expensive, usually a full stop-the-world pause (depending on the GC algorithm). A full GC also scans and evacuates survivors. Too many promotions = GC pressure.
Build a generational allocator
Simulate: allocate(obj) goes to Eden; minorGC() moves live Eden + active Survivor to the other Survivor; bump each object's age; if age > 2, promote to Old. Run 100 allocations with periodic minor GCs. Log the three regions and promotion events.
Allocation elimination
The JIT may scalar-replace an object — if escape analysis proves it never escapes the method, its fields go straight into registers or stack slots. No allocation, no GC. This is why tight loops with small helper objects can still be fast — the JVM sees through them.
Quiz: The generational hypothesis
Why does the JVM split Young vs Old? Empirically, most objects die young (temp strings, iteration variables, short-lived results). Collecting a small Young generation frequently is cheap; collecting a large Old generation rarely is fine. If you invalidated this hypothesis (e.g. giant long-lived caches), you'd need a different GC.