⚙️ JVM Internals & Memory · Advanced

Generational GC in Java

Young (eden, survivors) and old generations; minor vs major collections.

🧩 The mysteryYour service creates millions of objects per second, and the GC cleans them up in a few milliseconds. How can cleaning a huge area be so cheap?

Most objects die young

Look at a web request: temporary strings, iterators, DTOs, lambdas. Almost all of it is garbage milliseconds later. This weak generational hypothesis is why collectors split the heap into a young and an old generation.

The young generation

New objects are allocated in Eden. A minor GC collects only the young generation: it copies the few live objects into a survivor space, where they age with each GC they survive. There are two survivor spaces, used in turns.

🤔 Think first

What about the dead?

Eden holds 1 GB, but only 5 MB of it is still alive. What does the minor GC do with the 995 MB of dead objects?

Think about it, then reveal the answer

Nothing at all. It copies the 5 MB of live objects out, then simply reuses the whole Eden for new allocations. A copying collector's cost depends on the live objects, not the dead ones.

Promotion

Each survived young GC increases an object's age. Past the tenuring threshold, it's promoted (tenured) into the old generation. Minor GCs are frequent and cheap; major/full GCs also process the old generation and are rarer and costlier.

🔮 Predict it

Long-lived cache entry

A cache entry survives dozens of young collections. Where does it end up?

  1. The old generation
  2. Metaspace
  3. Deleted to make room
Show the answer

It's promoted to the old generation. Metaspace holds class metadata, not objects, and the GC never deletes reachable objects.

⚠️ The trap

Picturing two big boxes

Don't imagine the young and old generations as two contiguous blocks. G1, the default collector, splits the heap into many equal-sized regions. Each region is Eden, survivor, old or humongous, and a region's role can change over time.

💼 In the real world

Reading GC logs

Frequent, short young pauses are healthy. Trouble starts with medium-lived objects: a cache that keeps entries just long enough to be promoted, then drops them. They fill the old generation and trigger the expensive collections. Shorter lifetimes or a better eviction policy often help more than a bigger heap.

Key takeaways

  1. Young gen = Eden + two survivor spaces
  2. Objects that survive enough GCs are promoted to old gen
  3. Minor GC cost depends mostly on live objects, not dead ones
  4. G1 divides the heap into many equal-sized regions

💡 A nursery and a retirement home: most toddlers' toys are thrown out quickly, and only what lasts moves to the long-term shelf.

🤯 Did you know?

G1 treats any object of at least half a region's size as "humongous" and gives it its own contiguous regions, straight in the old generation.

Practice questions

Why is a minor GC usually cheap, even with a large Eden?

  1. It skips objects created in the last second
  2. Its cost depends on the few live objects it copies, and most young objects are already dead
  3. It never moves objects
  4. It only runs while the app is idle
Check your answer

Its cost depends on the few live objects it copies, and most young objects are already dead. A copying young collector evacuates only live objects; dead ones cost nothing because their space is simply reused.

An object keeps surviving young collections. What eventually happens to it?

  1. It is promoted (tenured) to the old generation
  2. It moves to metaspace
  3. It is moved to the thread stack
  4. It is deleted to make room
Check your answer

It is promoted (tenured) to the old generation. Each survived young GC increases the object's age. Past the tenuring threshold it is copied into the old generation.

G1 is just one of several collectors in the JDK. Next: Serial, Parallel, G1, ZGC and Shenandoah. Which one for which job?