Region-based collectors such as G1 size the young generation adaptively to meet a pause-time goal. On a production service, when would you override that with a fixed young-generation size, and what do you give up by doing so?
answer
- G1 young floats between G1NewSizePercent and G1MaxNewSizePercent
- -Xmn removes the pause goal's main lever
- pin for throughput batch or benchmarks, not latency services
- raise the floor instead of pinning the size
- absolute bytes do not scale across instance sizes; re-validate per JDK
basics
~20 sRarely. Pinning the young size with -Xmn disables the adaptive sizing that serves the pause goal, so the collector can no longer trade young size for latency as the workload shifts. Justify it only for throughput-only batch work, benchmarking reproducibility, or when adaptation demonstrably misbehaves - and prefer raising the floor instead.
solid answer
~50 sG1 sizes the young generation between -XX:G1NewSizePercent (5% by default) and -XX:G1MaxNewSizePercent (60%), choosing a size its pause-prediction model expects to fit -XX:MaxGCPauseMillis. Setting -Xmn, or NewSize equal to MaxNewSize, takes that away: the pause goal no longer influences young sizing at all. Defensible reasons to pin: a throughput-only batch job where pause length is irrelevant and you want the largest possible young generation; reproducible benchmarking where adaptation is a confound; a workload with a genuinely stable allocation profile where measurement shows the adaptive size oscillating badly; or a pathological case where an over-aggressive pause goal has collapsed young to the floor, causing collection storms. What you give up: responsiveness to phase changes such as startup, cache warming and traffic peaks; adherence to the latency target; and portability, because a fixed number is wrong on every instance size but the one it was measured on. Usually the better move is to raise the floor with G1NewSizePercent, or to relax the pause goal, and to leave the ceiling adaptive.
code
text · 8 lines# problem: young collapses to the 5% floor chasing an unrealistic goal
-XX:MaxGCPauseMillis=10
# better: an achievable goal plus a floor, ceiling left adaptive
-XX:MaxGCPauseMillis=100 -XX:G1NewSizePercent=25
# throughput-only batch: pinning is defensible here
-Xmn6g -XX:+UseParallelGCgo deeper
Know that modern collectors choose the young size themselves and that hand-setting it is not the normal starting point.
Explain that -Xmn fixes the young size and thereby removes the collector's main lever for meeting a pause target.
Recognise the collapsed-young-generation symptom of an unachievable pause goal and fix it with the floor or the goal rather than by pinning.
Set policy: declare goals not geometry, allow pinned layouts only as documented, measured exceptions with review dates and per-JDK re-validation.
## What adaptation is doing for you A pause-goal collector treats the young generation as its main latency lever. Because a young pause is dominated by the volume of surviving data copied, and that volume scales with how much was allocated since the last collection, the collector can shorten pauses by collecting a smaller young generation more often, or raise throughput by collecting a larger one less often. G1 maintains a prediction model from recent collections - copy cost per byte, root scanning, remembered-set work - and picks the young size it expects will land inside -XX:MaxGCPauseMillis, clamped between G1NewSizePercent and G1MaxNewSizePercent. That feedback loop is the reason a single flag set survives startup, warm-up, traffic peaks and a nightly batch inside the same process. It re-derives the split continuously; a static number cannot. ## What pinning actually does Setting -Xmn (or NewSize together with MaxNewSize) makes the young size a constant. The pause goal is not deleted - it still influences, for example, how many old regions are added to a mixed collection - but the dominant lever is gone. If your fixed size produces pauses above the target, the collector cannot correct it; if it produces pauses far below the target, it cannot reclaim the throughput you left on the table. There is a second, quieter cost: a fixed young size is expressed in absolute bytes, so the same command line is wrong when the service is moved to a larger instance or its heap is resized. Percentage-based flags scale; -Xmn does not. ## Where pinning is genuinely defensible Throughput-only workloads. A batch or ETL process where nobody is waiting on a response has no latency target to serve. Giving it the largest workable young generation minimises promotion and total GC work, and adaptation aimed at pause length is optimising for something you do not care about. Benchmarking and capacity experiments. When you are measuring the effect of a code change, adaptive sizing makes runs non-comparable; pinning removes a variable. This is a measurement decision, not necessarily a production one. Demonstrated pathological adaptation. The classic case is an unrealistically low pause goal: the collector shrinks young toward the 5% floor chasing a target it cannot hit, collections become extremely frequent, promotion soars and old-generation pressure follows. Here the real bug is the pause goal, and the correct fix is to relax it or raise the floor - pinning is the blunt instrument that also works. Stable, well-characterised allocation. Some services really do have a flat allocation profile with no phases. If measurement over a full traffic cycle shows the adaptive size sitting at one value anyway, pinning it costs little - but then it also gains little. ## The intermediate options a principal should reach for first Raise the floor, not the size: -XX:G1NewSizePercent sets a minimum young fraction, preventing collapse while leaving the collector free to grow. Lower the ceiling with G1MaxNewSizePercent if very large young collections are hurting the tail. Adjust -XX:MaxGCPauseMillis to a value your service actually needs, since an over-tight goal is the most common cause of bad adaptation. And check whether the underlying problem is allocation rate, which no split can fully compensate for. ## How to decide and how to hold the decision Treat any pinned generational geometry as a documented exception with an owner, a measurement that justified it, and a review date. Record the workload shape it was measured against and the instance size it assumes. Re-validate on every JDK upgrade: collector heuristics change between releases, and a flag that fixed a real problem on one version is frequently the thing making the next version slower. The default posture on a latency-sensitive service is to state the goal - pause target, heap bounds - and let the collector derive the layout.
- What typically happens when -XX:MaxGCPauseMillis is set far lower than the collector can achieve?The collector shrinks the young generation toward its floor trying to reduce copy work per pause, so collections become very frequent. Short-lived objects then survive multiple collections, promotion rises, the old generation fills with garbage and old or mixed collections increase - throughput drops and the pause target is still missed.
- Why prefer -XX:G1NewSizePercent over -Xmn when you want a bigger young generation?It sets a minimum rather than a fixed value, so the collector may still grow young when that serves the pause goal and shrink toward the floor when it does not. It is also expressed as a fraction of the heap, so the same setting remains sensible when the heap or instance size changes.
saying these in an interview costs you the question
- Pinning -Xmn on a latency-sensitive service without realising it disables pause-goal-driven young sizing
- Believing -XX:MaxGCPauseMillis is a guarantee rather than a target the collector optimises toward
- Copying absolute -Xmn values between services or instance sizes
- Adding generational flags before measuring allocation rate and survivor volume
- Carrying tuned flags across major JDK upgrades without re-validating them