skip to content

In a generational Java heap, how does enlarging the young generation change the frequency and the duration of minor collections, and when does a larger young generation actually reduce total GC overhead?

level: middleimportance: must knowfreq 60%

answer

  1. frequency = eden / allocation rate
  2. pause tracks survivors, not eden
  3. more time to die = less promotion
  4. young space is stolen from old
  5. -Xmn, -XX:NewRatio, G1NewSizePercent

basics

~20 s

A bigger young generation fills more slowly, so minor collections happen less often. Each collection copies only the survivors, so its pause tracks surviving data, not eden size. Because most objects die fast, a bigger young space usually means fewer collections and fewer survivors per collection.

solid answer

~50 s

Minor-collection frequency is roughly eden size divided by allocation rate: double the young generation and you roughly halve the number of minor GCs at the same allocation rate. Minor-collection cost is proportional to what survives, because the young collector evacuates the live objects and abandons everything else - dead objects cost nothing to reclaim. So a larger young generation does not automatically mean a proportionally longer pause. It often lowers total GC work for a second reason: more wall-clock time between collections lets more objects die before they are ever examined, so survivor volume and promotion both drop, which keeps garbage out of the old generation and reduces old/mixed collections. The costs: in a fixed total heap, young space is taken from old, shrinking room for the live set and for promotion headroom. And if the workload really does keep objects alive across collections (large request payloads, caches), survivors scale with eden and pauses genuinely grow. Flags: -Xmn pins young size, -XX:NewRatio sets the old:young ratio.

code

text · 8 lines
text
# ratio form: old is 2x young, so young = 1/3 of heap = 2g
java -Xms6g -Xmx6g -XX:NewRatio=2 -jar app.jar

# absolute form: pin young to 2g (sets NewSize and MaxNewSize)
java -Xms6g -Xmx6g -Xmn2g -jar app.jar

# G1: keep adaptive sizing, raise only the floor
java -Xms6g -Xmx6g -XX:G1NewSizePercent=25 -XX:MaxGCPauseMillis=100 -jar app.jar

go deeper

for a junior

Know that new objects go into eden, that filling eden triggers a minor collection, and that only survivors are copied out.

for a middle

Be able to state frequency = eden / allocation rate and cost proportional to survivors, and name -Xmn and -XX:NewRatio.

for a senior

Reason about the promotion side effect: a larger young generation lowers promoted bytes, and quantify the old-generation capacity you are trading away.

for a principal

Frame it as a latency-versus-throughput and capacity decision: allocation rate, live set, pause target and instance size together decide the split, and reducing allocation usually beats retuning it.

## The layout being tuned A generational heap splits the managed heap into a young generation, where nearly every new object is allocated, and an old generation, holding objects that have survived long enough to be promoted. The young generation is collected often (a minor or young collection); the old generation is collected rarely and more expensively. The HotSpot flags that set the split are -XX:NewRatio=N, meaning old is N times young (NewRatio=2 gives young = 1/3 of the heap), and -Xmn, which pins the young generation to an absolute size by setting NewSize and MaxNewSize together. Region-based G1 instead sizes young adaptively between -XX:G1NewSizePercent (default 5%) and -XX:G1MaxNewSizePercent (default 60%) of the heap. ## Why frequency scales with size Allocation is a pointer bump in eden. Eden fills at the application's allocation rate, and when it is full a young collection happens. So the interval between minor collections is approximately eden size / allocation rate. A service allocating 1 GB/s with a 500 MB eden collects roughly twice a second; give it a 2 GB eden and it collects once every two seconds. Frequency is linear in size, and that is the whole of the frequency story. ## Why pause length does not scale with size The young collector is a copying (evacuating) collector: it traces from roots into the young generation, copies each reachable object out to a survivor space or into the old generation, and then declares the whole of eden free in one step. Nothing is done per dead object. The dominant cost terms are therefore root scanning, the volume of live data copied, and the work of finding old-to-young references. Enlarging eden adds no copy work by itself. This is why the classic answer is that young pauses are governed by survivor volume, not by eden size. ## Why a bigger young generation usually reduces total work Most objects die very young, and object lifetimes are mostly measured in wall-clock time (a request finishes, a buffer is released), not in collections. Lengthening the interval between collections therefore means a larger fraction of each eden is already dead when the collector arrives. Two things follow: less data is copied per collection, and fewer objects survive enough collections to be promoted. Since promoted garbage is exactly what forces old-generation collections, a young generation comfortably larger than the working set of in-flight objects is the single most effective generational-layout change for an allocation-heavy service. ## Where it stops helping With a fixed -Xmx, every megabyte given to young is taken from old. Too little old space means the live set plus promotion headroom no longer fits, producing frequent old-generation cycles, evacuation failures, or full GCs - a worse outcome than frequent cheap minor GCs. If measured survivor volume grows in proportion to eden, the workload holds objects across the whole interval anyway (long-lived sessions, streaming buffers, a cache being filled) and you are simply buying longer pauses. And on a collector driven by a pause goal, pinning a large young size overrides the adaptive sizing that was protecting your latency target. ## How to decide in practice Measure allocation rate and survivor volume, then set the young generation so the interval between minor collections comfortably exceeds the lifetime of a typical request-scoped object, while leaving the old generation at least two to three times the steady-state live set. Verify by checking that the promoted bytes per collection fall, not just that the minor GC count fell.

  • You double the young generation and the minor GC pause doubles as well. What does that tell you about the workload?
    That survivor volume scaled with eden, so objects are still alive when the collector runs regardless of how long it waits. Copy cost is proportional to live data, so a proportional pause increase means the extra time did not let anything die. Typical causes are long-lived request state, a cache being populated, or a queue backing up - fix the retention rather than the sizing.
  • With a fixed -Xmx, what do you give up when you enlarge the young generation?
    Old-generation capacity. The old generation must hold the steady-state live set plus enough headroom to absorb promotion between old collections. Squeezing it causes more frequent old or mixed collections, and with a concurrent collector it risks the cycle not finishing before the space is exhausted, which degrades to a long stop-the-world full collection.

Eden is a waiting room you empty by carrying out only the people still waiting. A bigger room means you empty it less often, and because most people leave on their own while you wait, you also carry fewer out each time.

saying these in an interview costs you the question

  • Claiming minor GC pause is proportional to eden size rather than to surviving data
  • Saying a bigger young generation always means longer pauses
  • Thinking a young collection scans the entire heap
  • Shrinking the young generation to reduce promotion (it increases it)
  • Pinning -Xmn on a pause-goal collector without knowing it disables adaptive young sizing

context