Consult this before recommending any flag. Every entry was verified by running -XX:+PrintFlagsFinal and by actually launching the JVM on Temurin 8u504, 11.0.32.1, 17.0.20.1, 21.0.12 and 25.0.4.1 (Windows x64).
This file exists because the single most common defect in Java performance advice is a flag that no longer exists. A removed flag does not degrade gracefully - in most cases the JVM refuses to start.
The classification records what launching a JVM establishes, and nothing more. Starting a JVM with a flag shows whether it starts and what it prints. It does not show whether the flag had any effect. Those are separate questions and this file keeps them separate, because conflating them is how flag advice goes wrong.
| Classification | What launching the JVM showed |
|---|---|
| start | Starts, prints nothing about the flag |
| warn-dep | Starts, prints a deprecation warning. The flag still does its job unless the Effect column says otherwise |
| warn-ign | Starts, prints a warning that says the option is being ignored. The JVM itself tells you it has no effect |
| warn | Starts, prints some other warning (usually environmental, e.g. large pages unavailable) |
| absent | "Unrecognized VM option". The JVM prints an error and does not start |
| bad-value | Recognised, value format rejected. The JVM does not start |
| needs-unlock | Recognised only after -XX:+UnlockDiagnosticVMOptions or -XX:+UnlockExperimentalVMOptions; without it the JVM does not start |
| needs-companion | Recognised, but the JVM refuses to start until a companion flag or value is supplied |
On effect. A separate Effect column appears only where the effect was behaviourally tested - by observing what the JVM then did, not by reading the flag back. Where that column is blank, the effect was not established here and no claim is made. Three specific warnings:
warn-depdoes not mean inert.-XX:MaxRAMFraction=8prints a deprecation warning on 11, 17 and 21 and still halves the heap (measured: 8516 MB default to 4259 MB). Treating a deprecation warning as "harmless, it is ignored" is a live source of error.- Absence of a warning does not mean effect.
-XX:CompileThreshold=8000starts silently on all five versions and sets the flag, but the thresholds that actually gate compilation under tiered compilation (Tier3InvocationThreshold200,Tier4InvocationThreshold5000) are unchanged. Reading a flag back withPrintFlagsFinalproves it was accepted, never that it was used. - A warning-free flag can be actively harmful.
-XX:+DisableExplicitGCstarts silently everywhere and breaks directByteBufferreclamation - see garbage-collection.md.
Everything here is Temurin on Windows x64. That is a real limitation for three families of flag, and it is stated here rather than in a footnote because it changes what the tables can be used for:
- Container and cgroup flags do not exist on this platform at all.
-XX:+UseContainerSupport,-XX:+PreferContainerQuotaForCPUCountand-XX:+UseTransparentHugePageswere "Unrecognized VM option" on all five versions, including with both unlock flags set. That is a Windows fact, not a version fact, and these tables cannot distinguish "removed in version N" from "Linux-only" for such flags. Where this file states a container boundary, it is cited from the JDK issue and marked as not measured. - Large pages (
-XX:+UseLargePages) is recognised and returnswarnon all five, because the OS has not been configured for it. Behaviour was not exercised. - Flags marked (pd) are platform-dependent by definition, and the default heap and thread figures below are functions of this host's 8 CPUs and 32 GB.
Re-measured by launching each JVM with each flag and recording exit status and warning text. The Effect column is populated only where behaviour was separately tested; blank means not established here.
| Flag | 8 | 11 | 17 | 21 | 25 | Effect |
|---|---|---|---|---|---|---|
-XX:+UseConcMarkSweepGC |
start | warn-dep | absent | absent | absent | |
-XX:CMSInitiatingOccupancyFraction=N |
start | start | absent | absent | absent | |
-XX:+CMSClassUnloadingEnabled |
start | start | absent | absent | absent | |
-XX:+CMSPermGenSweepingEnabled |
start | absent | absent | absent | absent | |
-XX:+UseParNewGC |
warn-dep | absent | absent | absent | absent | effective on 8, and harmful ⁴ |
-XX:+UseParallelOldGC |
start | start | absent | absent | absent | effective (already the default with Parallel) |
-XX:+AggressiveOpts |
start | warn-dep | absent | absent | absent | |
-XX:MaxPermSize=N / -XX:PermSize=N |
warn-ign | warn-ign | absent | absent | absent | none - the JVM says so ⁵ |
-XX:+UseBiasedLocking |
start | start | warn-dep | absent | absent | |
-XX:MaxRAMFraction=N |
start | warn-dep | warn-dep | warn-dep | absent | effective on 8-21 ⁶ |
-XX:+PrintGCDetails |
start | warn-dep | warn-dep | warn-dep | warn-dep | mapped to -Xlog:gc* |
-Xloggc:file |
start | warn-dep | warn-dep | warn-dep | warn-dep | mapped to -Xlog:gc:file=… |
-XX:+PrintGCTimeStamps |
start | absent | absent | absent | absent | |
-XX:+PrintGCDateStamps |
start | absent | absent | absent | absent | |
-XX:+UseGCLogFileRotation |
start | absent | absent | absent | absent | |
-XX:NumberOfGCLogFiles=N |
start | absent | absent | absent | absent | |
-XX:GCLogFileSize=N |
start | absent | absent | absent | absent | |
-XX:+PrintTenuringDistribution |
start | absent | absent | absent | absent | |
-XX:+PrintReferenceGC |
start | absent | absent | absent | absent | |
-XX:+PrintAdaptiveSizePolicy |
start | absent | absent | absent | absent | |
-XX:+PrintTLAB |
start | absent | absent | absent | absent | |
-XX:+PrintStringDeduplicationStatistics |
start | absent | absent | absent | absent | |
-XX:+PrintSafepointStatistics |
start | warn-dep | absent | absent | absent | use -Xlog:safepoint |
-XX:+TraceClassLoading |
start | warn-dep | absent | absent | absent | use -Xlog:class+load |
-XX:+UnlockCommercialFeatures |
start | absent | absent | absent | absent | |
-XX:+UseFastAccessorMethods |
start | absent | absent | absent | absent | |
-XX:+UseStringCache |
warn-ign | absent | absent | absent | absent | none - the JVM says so |
-XX:-AutoShutdownNMT |
warn-ign | absent | absent | absent | absent | none on 8; flag gone from 11 |
-XX:+ScavengeBeforeFullGC |
start | start | start | start | absent | |
-XX:-UseCounterDecay |
start | start | start | start | absent | |
-XX:+UseSpinning |
absent | absent | absent | absent | absent | never existed in these builds |
-XX:MaxFreqInlineSize=N |
absent | absent | absent | absent | absent | erratum - the flag is FreqInlineSize |
-XX:AOTLibrary=path |
absent | needs-unlock¹ | absent | absent | absent | |
-XX:+UseAOT |
absent | needs-unlock¹ | absent | absent | absent | |
-XX:+ZGenerational |
absent | absent | absent | start | warn-ign | none on 25 - "removed in 24.0" |
-XX:+UseZGC |
absent | needs-unlock² | start | start | start | |
-XX:+UseShenandoahGC |
absent | start | start | start | start | vendor-dependent ⁷ |
-XX:+UseEpsilonGC |
absent | needs-unlock | needs-unlock | needs-unlock | needs-unlock | |
-XX:MaxRAMPercentage=75 |
bad-value | start | start | start | start | |
-XX:MaxRAMPercentage=75.0 |
start | start | start | start | start | effective |
-XX:-CompactStrings |
absent | start | start | start | start | |
-XX:+UseCompressedClassPointers |
start | start | start | start | warn-dep | deprecated by compact headers |
-XX:+UseCompactObjectHeaders |
absent | absent | absent | absent | start | effective - measured in allocation.md |
-XX:-ShowCodeDetailsInExceptionMessages |
absent | absent | start | start | start | |
-XX:+EnableDynamicAgentLoading |
absent | start | start | start | start | |
-XX:ArchiveClassesAtExit=f |
absent | absent | start | start | start | |
-XX:+AutoCreateSharedArchive |
absent | absent | absent | start | warn | needs SharedArchiveFile ⁸ |
-XX:AOTCache / AOTMode / AOTCacheOutput / AOTConfiguration |
absent | absent | absent | absent | start | ⁹ |
-XX:SoftMaxHeapSize=N |
absent | absent | start | start | start | |
-XX:+ZUncommit |
absent | absent | start | start | start | |
-XX:+G1UseAdaptiveIHOP |
absent | start | start | start | start | |
-d64 |
start | absent | absent | absent | absent | |
-client / -server |
start | start | start | start | start | no-op on 64-bit ¹⁰ |
-XX:+FlightRecorder |
start | start | warn-dep | warn-dep | warn-dep | JFR is always available now |
-XX:CompileThreshold=N |
start | start | start | start | start | none under tiered ¹¹ |
-XX:+PrintInlining |
needs-unlock | needs-unlock | needs-unlock | needs-unlock | needs-unlock | diagnostic |
-XX:GuaranteedSafepointInterval=N |
needs-unlock | needs-unlock | needs-unlock | needs-unlock | needs-unlock | diagnostic |
-XX:+EnableJVMCI |
absent | needs-unlock | needs-unlock | needs-unlock | needs-unlock | not confirmed to compile ⁷ |
-XX:+UseContainerSupport |
absent | absent | absent | absent | absent | Linux only - see platform caveat |
-XX:+PreferContainerQuotaForCPUCount |
absent | absent | absent | absent | absent | removed in 14; Linux only ¹² |
-XX:+UseTransparentHugePages |
absent | absent | absent | absent | absent | Linux only |
-XX:+UseLargePages |
warn | warn | warn | warn | warn | OS not configured on this host |
-XX:FreqInlineSize=N |
start | start | start | start | start | |
-XX:+UseStringDeduplication |
start | start | start | start | start | |
-XX:+DisableExplicitGC |
start | start | start | start | start | effective, and breaks direct buffers ¹³ |
-XX:+ExplicitGCInvokesConcurrent |
start | start | start | start | start |
¹ On 11, -XX:+UseAOT needs -XX:+UnlockExperimentalVMOptions; with it the JVM reports mixed mode, aot and jaotc is present. Absent from 17 onward.
² On 11, ZGC needs -XX:+UnlockExperimentalVMOptions. From 17 it is a plain product flag.
⁴ This is not "ignored", and the correction matters. On 8u504, -XX:+UseParNewGC prints a deprecation warning and then changes the collector pair: UseParallelGC and UseParallelOldGC both flip from true to false, and the GC log moves from PSYoungGen/ParOldGen to ParNew/Tenured - the single-threaded old-generation collector. The JVM says as much: "Using the ParNew young collector with the Serial old collector is deprecated". Leaving this flag on a Java 8 command line therefore costs you parallel full GCs, which on a large heap is seconds of pause. Remove it; do not assume it is inert.
⁵ The warning is explicit: "ignoring option MaxPermSize=128m; support was removed in 8.0". This is the one family where "accepted but no effect" is established, because the JVM states it.
⁶ Measured: default MaxHeapSize 8516 MB, with -XX:MaxRAMFraction=8 it becomes 4259 MB, on 11, 17 and 21 alike. The deprecation warning does not mean the flag stopped working - it means it will be removed, and on 25 it is.
⁷ Build-level, not version-level. Shenandoah is product in Adoptium builds and absent from Oracle's. For JVMCI, only startup and module presence were confirmed, not that Graal compiled anything.
⁸ Bare -XX:+AutoCreateSharedArchive refuses to start: "requires -XX:SharedArchiveFile". With one supplied it starts on 21 and starts with a warning on 25.
⁹ -XX:AOTMode=record alone refuses to start: "At least one of AOTCacheOutput and AOTConfiguration must be specified". See startup-and-warmup.md for the working sequences.
¹⁰ Both accepted on all five, and both yield "OpenJDK 64-Bit Server VM" on 8u504 - there is no client VM on 64-bit. Accepted, no warning, no effect.
¹¹ Accepted silently on all five and the flag reads back as set, but Tier3InvocationThreshold (200) and Tier4InvocationThreshold (5000) are unchanged, and those are what gate compilation while TieredCompilation is true - which it is by default everywhere.
¹² Existed only in 10-13 as the switch for the quota-only transition, and was removed in JDK 14 (JDK-8226575); shares are now ignored unconditionally. Absent here on every version, but that is also what Windows reports for every container flag, so the removal is cited, not measured.
¹³ Starts silently on all five, and on all five it turns OutOfMemoryError: Direct buffer memory from something that cannot happen into something that happens after exactly one cap's worth of allocation. See garbage-collection.md.
| Upgrade | What breaks first |
|---|---|
| 8 → 11 | Every Print* GC flag except PrintGCDetails; the GC log rotation trio (UseGCLogFileRotation, NumberOfGCLogFiles, GCLogFileSize); UseParNewGC; CMSPermGenSweepingEnabled; UnlockCommercialFeatures; UseStringCache; AutoShutdownNMT; UseFastAccessorMethods; -d64 |
| 11 → 17 | All CMS flags (including CMSClassUnloadingEnabled); UseParallelOldGC; AggressiveOpts; PermSize/MaxPermSize; PrintSafepointStatistics; TraceClassLoading |
| 17 → 21 | UseBiasedLocking |
| 21 → 25 | MaxRAMFraction; ScavengeBeforeFullGC; UseCounterDecay; ZGenerational becomes ignored; UseCompressedClassPointers becomes deprecated |
11 → 17 is the most dangerous step for anything CMS-tuned, and 8 → 11 for anything with Java 8 GC logging. Note the asymmetry that makes these survive: MaxPermSize and MaxRAMFraction are silently tolerated for two to three LTS releases before turning fatal, so they sit in scripts for years.
Measured on an 8-CPU, 32 GB Windows x64 host with no other flags. Values marked (pd) are platform-dependent.
| Flag | 8 | 11 | 17 | 21 | 25 |
|---|---|---|---|---|---|
InitialHeapSize (pd) |
508 M | 508 M | 508 M | 508 M | 508 M |
MaxHeapSize (pd) |
8122 M | 8122 M | 8124 M | 8124 M | 8124 M |
MaxRAMPercentage |
25.0 | 25.0 | 25.0 | 25.0 | 25.0 |
InitialRAMPercentage |
1.5625 | 1.5625 | 1.5625 | 1.5625 | 1.5625 |
MinRAMPercentage |
50.0 | 50.0 | 50.0 | 50.0 | 50.0 |
NewRatio |
2 | 2 | 2 | 2 | 2 |
SurvivorRatio |
8 | 8 | 8 | 8 | 8 |
TargetSurvivorRatio |
50 | 50 | 50 | 50 | 50 |
InitialTenuringThreshold |
7 | 7 | 7 | 7 | 7 |
MaxTenuringThreshold |
15 | 15 | 15 | 15 | 15 |
MetaspaceSize |
~20 M | ~20 M | ~21 M | ~21 M | ~21 M |
MaxMetaspaceSize |
unlimited | unlimited | unlimited | unlimited | unlimited |
MaxDirectMemorySize |
0 (= -Xmx) |
0 | 0 | 0 | 0 |
UseCompressedOops |
true | true | true | true | true |
UseCompactObjectHeaders |
absent | absent | absent | absent | false |
AlwaysPreTouch |
false | false | false | false | false |
UseLargePages |
false | false | false | false | false |
SoftRefLRUPolicyMSPerMB |
1000 | 1000 | 1000 | 1000 | 1000 |
GCTimeLimit |
98 | 98 | 98 | 98 | 98 |
GCHeapFreeLimit |
2 | 2 | 2 | 2 | 2 |
UseGCOverheadLimit |
true | true | true | true | true |
MaxRAM (pd) |
128 G | 128 G | 128 G | 128 G | 128 G |
NewSize (pd) |
169 M | 1.3 M | 1.3 M | 1.3 M | 1.3 M |
MaxNewSize (pd) |
2707 M | 4872 M | 4872 M | 4872 M | 4872 M |
InitialSurvivorRatio |
8 | 8 | 8 | 8 | 8 |
MinSurvivorRatio |
3 | 3 | 3 | 3 | 3 |
AlwaysTenure / NeverTenure |
false | false | false | false | false |
TLABSize |
0 (adaptive) | 0 | 0 | 0 | 0 |
MinTLABSize |
2048 | 2048 | 2048 | 2048 | 2048 |
HeapDumpBeforeFullGC / HeapDumpAfterFullGC |
false | false | false | false | false |
NewSize and MaxNewSize differ sharply between 8 and 11+ because the default collector differs: Parallel GC on 8 sizes a fixed young generation eagerly, G1 on 11+ sizes regions adaptively and starts far smaller. This is the clearest illustration of why a default quoted without its collector is meaningless.
| Flag | 8 | 11 | 17 | 21 | 25 |
|---|---|---|---|---|---|
UseParallelGC |
true | false | false | false | false |
UseG1GC |
false | true | true | true | true |
UseSerialGC |
false | false | false | false | false |
UseZGC |
absent | false (exp) | false | false | false |
ZGenerational |
absent | absent | absent | false | absent |
UseShenandoahGC |
absent | false | false | false | false |
UseEpsilonGC |
absent | false (exp) | false (exp) | false (exp) | false (exp) |
MaxGCPauseMillis |
unset¹ | 200 | 200 | 200 | 200 |
GCTimeRatio |
99 | 12 | 12 | 12 | 12 |
ParallelGCThreads (pd) |
8 | 8 | 8 | 8 | 8 |
ConcGCThreads (pd) |
0 | 2 | 2 | 2 | 2 |
InitiatingHeapOccupancyPercent |
45 | 45 | 45 | 45 | 45 |
G1HeapRegionSize (pd) |
0² | 2 M | 4 M | 4 M | 4 M |
G1MixedGCCountTarget |
8 | 8 | 8 | 8 | 8 |
UseAdaptiveSizePolicy |
true | true | true | true | true |
UseStringDeduplication |
false | false | false | false | false |
StringDeduplicationAgeThreshold |
3 | 3 | 3 | 3 | 3 |
UseTLAB / ResizeTLAB |
true | true | true | true | true |
¹ Reads 18446744073709551615 (ULONG_MAX), i.e. no pause goal - Parallel GC ships none.
² Computed later on 8; eagerly on 11+. Note the ergonomics changed between 11 and 17.
| Flag | 8 | 11 | 17 | 21 | 25 |
|---|---|---|---|---|---|
TieredCompilation |
true | true | true | true | true |
TieredStopAtLevel |
4 | 4 | 4 | 4 | 4 |
ReservedCodeCacheSize |
240 M | 240 M | 240 M | 240 M | 240 M |
InitialCodeCacheSize |
2.44 M | 2.44 M | 2.44 M | 2.44 M | 2.44 M |
SegmentedCodeCache |
absent | true | true | true | true |
NonProfiledCodeHeapSize |
absent | ~117 M | ~117 M | ~117 M | ~117 M |
ProfiledCodeHeapSize |
absent | ~117 M | ~117 M | ~117 M | ~117 M |
NonNMethodCodeHeapSize |
absent | ~5.6 M | ~5.6 M | ~5.6 M | ~5.6 M |
CICompilerCount (pd) |
4 | 4 | 4 | 4 | 4 |
CompileThreshold (inert) |
10000 | 10000 | 10000 | 10000 | 10000 |
Tier3InvocationThreshold |
200 | 200 | 200 | 200 | 200 |
Tier4InvocationThreshold |
5000 | 5000 | 5000 | 5000 | 5000 |
MaxInlineSize |
35 | 35 | 35 | 35 | 35 |
FreqInlineSize |
325 | 325 | 325 | 325 | 325 |
DoEscapeAnalysis |
true | true | true | true | true |
EliminateAllocations |
true | true | true | true | true |
BackgroundCompilation |
true | true | true | true | true |
UseAVX (pd) |
2 | 2 | 2 | 2 | 2 |
UseSSE (pd) |
4 | 4 | 4 | 4 | 4 |
EnableJVMCI / UseJVMCICompiler |
absent | false | false | false | false |
| Flag | 8 | 11 | 17 | 21 | 25 |
|---|---|---|---|---|---|
CompactStrings |
absent | true | true | true | true |
StringTableSize |
60013 | 65536 | 65536 | 65536 | 65536 |
AutoBoxCacheMax |
128 | 128 | 128 | 128 | 128 |
StackTraceInThrowable |
true | true | true | true | true |
EnableContended |
true | true | true | true | true |
RestrictContended |
true | true | true | true | true |
PrintCompilation |
false | false | false | false | false |
DumpLoadedClassList |
✓ | ✓ | ✓ | ✓ | ✓ |
SharedClassListFile |
✓ | ✓ | ✓ | ✓ | ✓ |
SharedArchiveFile |
✓ (diag) | ✓ | ✓ | ✓ | ✓ |
ArchiveClassesAtExit |
absent | absent | ✓ | ✓ | ✓ |
AutoCreateSharedArchive |
absent | absent | absent | false | false |
UseSharedSpaces |
false | false | true | absent | absent |
AOTCache / AOTMode / AOTCacheOutput |
absent | absent | absent | absent | ✓ |
AOTClassLinking |
absent | absent | absent | absent | false |
AutoShutdownNMT |
✓ | ✓ | absent | absent | absent |
ActiveProcessorCount |
−1 | −1 | −1 | −1 | −1 |
UseContainerSupport |
Linux only | Linux only | Linux only | Linux only | Linux only |
Found by running the flags the book documents, and located in the source text to establish which edition carries each.
1. -XX:MaxFreqInlineSize does not exist. Named for the frequent-inlining threshold with a default of 325. Verified absent from all five versions; the real flag is FreqInlineSize, default 325 on every version. Using the book's name means the JVM will not start. This one is in both editions - 1st ed at the inlining discussion, 2nd ed in both the body and the index - so it is not a 2nd-edition slip and it has been repeated for a decade.
2. -XX:ProfiledCodeHapSize and -XX:NonProfiledCodeHapSize are misspelled - "Hap" for "Heap". Verified absent from all five; the real flags are ProfiledCodeHeapSize and NonProfiledCodeHeapSize. 2nd edition only, since the segmented code cache is not covered in the 1st. The book spells the third one, NonNMethodCodeHeapSize, correctly, which is what makes the typo easy to miss.
3. Appendix A lists -XX:HeapFreeLimit, which does not exist on any version; the body text correctly uses GCHeapFreeLimit, verified at 2 on all five. 2nd edition appendix and index.
Almost everything above is context for not setting things. This is the short list.
-XX:MaxRAMPercentage=75.0 # containers; note the decimal
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=8,filesize=16M
-XX:StartFlightRecording=maxsize=256m,maxage=6h,settings=default
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log
-XX:+ExitOnOutOfMemoryError # containers
-XX:NativeMemoryTracking=summary # ~5-10% footprint, worth itNone of these tunes anything. They make the next incident diagnosable, which is the highest-value thing you can do before you have a measurement.
| Flag | Requires |
|---|---|
-Xmx / -Xms |
Occupancy after a full GC |
-XX:MaxGCPauseMillis=N |
GC log showing pauses near but above target |
-XX:ReservedCodeCacheSize=N |
"CodeCache is full" warning, or Compiler.codecache |
-XX:+UseParallelGC |
CPU-bound batch work, pauses irrelevant |
-XX:+UseZGC |
A pause-time SLO, plus spare CPU and heap |
-XX:ParallelGCThreads=N |
Several JVMs per host, or a very large machine |
-XX:MaxDirectMemorySize=N |
A footprint ceiling that matters |
-XX:StringTableSize=N |
PrintStringTableStatistics showing large buckets |
-XX:+UseCompactObjectHeaders |
25 only; measured benefit on your objects |
-XX:TieredStopAtLevel=1 |
A genuinely short-lived process |
-XX:+AlwaysPreTouch |
Preferring a deploy-time failure to a later one |
-XX:ActiveProcessorCount=N |
Only a CPU request is set, or detection is wrong |
| Flag | Why |
|---|---|
-XX:+AggressiveOpts |
Removed in 12; fatal from 17 |
-XX:+UseBiasedLocking |
Fatal from 21 |
-XX:MaxRAMFraction |
Fatal on 25; use MaxRAMPercentage |
-XX:CompileThreshold |
Inert under tiered compilation |
-XX:-TieredCompilation |
Only to save code cache in a memory-starved JVM |
-XX:-DoEscapeAnalysis |
Disables a large correct optimisation |
-XX:-StackTraceInThrowable |
Removes diagnosability everywhere |
-XX:+AggressiveHeap |
Hides what it sets; values are stale |
-XX:-UseAdaptiveSizePolicy |
What makes the defaults work |
| Anything not in this file or in the coverage table below | If you cannot find it in this file, do not recommend it |
-XX:+AggressiveHeap deserves its own note: it silently sets Xmx, Xms, NewSize, UseLargePages, ResizeTLAB, TLABSize, UseParallelGC, YoungPLABSize, OldPLABSize, ThresholdTolerance, ScavengeBeforeFullGC and BindGCTaskThreadsToCPUs at once. Several are now set better ergonomically, one (ScavengeBeforeFullGC=false) is actively harmful in the general case, and because it hides everything you cannot tell what you have configured. Set the specific flags you need.
Operating rule 1 says not to recommend a flag that is not in this file, so this table exists to make that rule checkable rather than aspirational. It lists every -XX flag named anywhere in this skill, and where in this file its status is recorded. It was generated by extracting flag names from every file and diffing against this one, so a flag recommended elsewhere and missing here is a defect in the skill, not a judgement call.
| Family | Flags | Recorded in |
|---|---|---|
| Removed or version-gated | UseConcMarkSweepGC, CMSInitiatingOccupancyFraction, CMSClassUnloadingEnabled, CMSPermGenSweepingEnabled, UseParNewGC, UseParallelOldGC, AggressiveOpts, MaxPermSize, PermSize, UseBiasedLocking, MaxRAMFraction, all Java 8 Print* GC flags, UseGCLogFileRotation, NumberOfGCLogFiles, GCLogFileSize, UnlockCommercialFeatures, UseSpinning, MaxFreqInlineSize, AOTLibrary, UseAOT, ZGenerational, UseStringCache, AutoShutdownNMT, UseFastAccessorMethods, PrintSafepointStatistics, TraceClassLoading, ScavengeBeforeFullGC, UseCounterDecay, -d64, -client/-server |
the removal matrix |
| Version-gated additions | CompactStrings, UseCompactObjectHeaders, UseCompressedClassPointers, ShowCodeDetailsInExceptionMessages, EnableDynamicAgentLoading, ArchiveClassesAtExit, AutoCreateSharedArchive, AOTCache, AOTMode, AOTCacheOutput, AOTConfiguration, SoftMaxHeapSize, ZUncommit, G1UseAdaptiveIHOP, UseZGC, UseShenandoahGC, UseEpsilonGC |
the removal matrix |
| Explicit collection | DisableExplicitGC, ExplicitGCInvokesConcurrent |
the removal matrix, and garbage-collection.md |
| Linux-only | UseContainerSupport, PreferContainerQuotaForCPUCount, UseTransparentHugePages |
the platform caveat and the removal matrix |
| Heap and GC defaults | MaxRAMPercentage, InitialRAMPercentage, MinRAMPercentage, MaxRAM, NewRatio, NewSize, MaxNewSize, SurvivorRatio, InitialSurvivorRatio, MinSurvivorRatio, TargetSurvivorRatio, InitialTenuringThreshold, MaxTenuringThreshold, AlwaysTenure, NeverTenure, MetaspaceSize, MaxMetaspaceSize, MaxDirectMemorySize, SoftRefLRUPolicyMSPerMB, GCTimeLimit, GCHeapFreeLimit, UseGCOverheadLimit, MaxGCPauseMillis, GCTimeRatio, ParallelGCThreads, ConcGCThreads, InitiatingHeapOccupancyPercent, G1HeapRegionSize, G1MixedGCCountTarget, UseAdaptiveSizePolicy, UseDynamicNumberOfGCThreads, UseStringDeduplication, StringDeduplicationAgeThreshold, UseTLAB, ResizeTLAB, TLABSize, MinTLABSize, AlwaysPreTouch, UseLargePages, UseCompressedOops, UseNUMA |
Verified defaults |
| Compiler | TieredCompilation, TieredStopAtLevel, ReservedCodeCacheSize, InitialCodeCacheSize, SegmentedCodeCache, NonProfiledCodeHeapSize, ProfiledCodeHeapSize, NonNMethodCodeHeapSize, CICompilerCount, CompileThreshold, Tier3InvocationThreshold, Tier4InvocationThreshold, MaxInlineSize, FreqInlineSize, DoEscapeAnalysis, EliminateAllocations, EliminateLocks, OptimizeStringConcat, BackgroundCompilation, UseCodeCacheFlushing, UseAVX, UseSSE, EnableJVMCI, UseJVMCICompiler, PrintCompilation, PrintInlining |
Verified defaults, Compiler |
| Diagnostics and lifecycle | HeapDumpOnOutOfMemoryError, HeapDumpPath, HeapDumpBeforeFullGC, HeapDumpAfterFullGC, ExitOnOutOfMemoryError, NativeMemoryTracking, StartFlightRecording, FlightRecorder, StackTraceInThrowable, ActiveProcessorCount, PerfDisableSharedMem, DisableAttachMechanism, UnlockDiagnosticVMOptions, UnlockExperimentalVMOptions, GuaranteedSafepointInterval, UseCountedLoopSafepoints, ClassUnloading |
Verified defaults, and the removal matrix where version-gated |
| Strings, boxing, layout | StringTableSize, PrintStringTableStatistics, AutoBoxCacheMax, ObjectAlignmentInBytes, EnableContended, RestrictContended |
Verified defaults |
| CDS and AOT paths | SharedArchiveFile, SharedClassListFile, DumpLoadedClassList, UseSharedSpaces, AOTClassLinking |
Verified defaults, and startup-and-warmup.md |
| Named only to warn against | AggressiveHeap, RunReallyFast (which does not exist, and is the point of rule 1) |
Flags to actively avoid |
# include your other options - flags affect each other, GC flags especially
java <your other options> -XX:+PrintFlagsFinal -version | grep MaxHeapSize
# every flag including diagnostic and experimental
java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions \
-XX:+PrintFlagsFinal -version
# on a running JVM
jcmd <pid> VM.flags -all
jinfo -flag MaxHeapSize <pid>In the output, := marks a non-default value (set on the command line, set indirectly by another flag, or computed ergonomically) and = marks the built-in default. The trailing brace gives the category: product, pd product (platform-dependent), manageable (changeable at runtime via jinfo), experimental (needs -XX:+UnlockExperimentalVMOptions), diagnostic (needs -XX:+UnlockDiagnosticVMOptions), C2 product.
There are 876 flags on 8u504, 946 on 11.0.32.1, 874 on 17.0.20.1, 881 on 21.0.12 and 915 on 25.0.4.1, counting assignment lines in
java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version. Counting only lines carrying a category brace instead gives 873/942/869/877/909, so treat these as ±5, not exact - the method is stated so you can reproduce or dispute it. The overwhelming majority exist so support engineers can extract information from a misbehaving JVM. Finding a flag in that list is not a reason to set it.
The whole file is a version matrix, so this section records only how the flag surface itself changes, which is what determines whether an upgrade breaks on startup.
| 8 | 11 | 17 | 21 | 25 | |
|---|---|---|---|---|---|
| Total flags (diagnostic + experimental unlocked) | 876 | 946 | 874 | 881 | 915 |
| Legacy flag handling | tolerant | mixed | strict | strict | strict |
| GC logging flag family | legacy Print* |
unified -Xlog |
-Xlog |
-Xlog |
-Xlog |
| Flags fatal that were fine on the previous LTS | - | 8 of those tested | 5 | 1 | 2 |
Two structural observations from that table:
- Java 11 has the largest flag count because it carries both the new unified-logging machinery and much of the old GC surface that 17 removed. It is the most permissive release for a legacy command line, and therefore the one that lets stale flags survive into a 17 upgrade unnoticed.
- Java 17 is the strict boundary. Five of the flags tested here go from accepted to fatal at 17, including the entire CMS family. Where an upgrade is going to fail on a command line, it fails there.
The safe procedure for any upgrade is: run the target JVM with the existing command line and -version first. A fatal flag surfaces immediately and costs nothing to find. That single step catches every red row in the removal matrix.
Stated plainly, because the rest of this file claims verification.
- Container detection.
UseContainerSupportand cgroup behaviour are Linux mechanisms and absent from these Windows builds. The CPU cascade was verified using-XX:ActiveProcessorCount, which reproduces what a quota does to the JVM's view, but cgroup detection itself was not. - Linux-only page flags.
UseTransparentHugePagesdoes not exist in these builds.UseLargePagesexists and readsfalse, but large-page behaviour was not exercised. - Whether the Graal JIT compiles anything.
-XX:+UseJVMCICompilerstarts successfully on 11, 17 and 25, and the module is present (renamedjdk.internal.vm.compiler→jdk.graal.compileron 25). Whether it was actually used for compilation was not confirmed. - Other vendors. Everything is Temurin. Shenandoah in particular is a build-level difference -
productin Adoptium, absent from Oracle's builds. Check the vendor. - Other platforms. All Windows x64. Flags marked (pd) differ by platform, and the default heap sizes above are functions of this host's 32 GB and 8 CPUs.
- Update-version boundaries. Verified on the specific builds named. Statements like "8u192+ for container awareness" and "8u262+ for JFR" come from the release history, not from testing each update.
- Agent recommends a flag from memory or from older material without checking it here - most removed flags stop the JVM from starting
- Agent assumes a removed flag is ignored - the only flags where the JVM itself says it is ignoring the option are
MaxPermSize/PermSize,UseStringCacheandAutoShutdownNMT(on 8), andZGenerational(on 25). Everything else either works or stops the JVM starting - Agent assumes
-XX:+UseParNewGCon Java 8 is inert - it is not: it disablesUseParallelOldGCand substitutes the single-threaded old collector. Remove it rather than leaving it - Agent reads a deprecation warning as "ignored" -
MaxRAMFractionwarns from 11 and still halves the heap through 21 - Agent proves a flag "works" by reading it back from
PrintFlagsFinal- that proves acceptance, not effect;CompileThresholdreads back set and changes nothing under tiered compilation - Agent copies a Java 8 GC logging block forward - every
Print*flag exceptPrintGCDetailsis fatal from 11 - Agent copies a CMS-tuned command line to 17+ - every
CMS*flag is fatal there - Agent writes
-XX:MaxRAMPercentage=75- fatal on Java 8; the flag is adouble, so write75.0 - Agent uses
-XX:MaxRAMFraction- accepted on 8-21 and fatal on 25 - Agent recommends
-XX:MaxFreqInlineSize- does not exist on any version; the flag isFreqInlineSize. Erratum in Oaks' book - Agent recommends
-XX:ProfiledCodeHapSize- misspelled in the book; the real flag containsHeap - Agent recommends
-XX:CompileThreshold- inert under tiered compilation - Agent enables ZGC on 11 without
-XX:+UnlockExperimentalVMOptions- verified fatal without it - Agent sets
-XX:+ZGenerationalon 25 - accepted but ignored; only meaningful on 21 - Agent recommends Shenandoah without checking the vendor - absent from Oracle builds
- Agent quotes a default without stating the host - heap and thread defaults derive from CPU count and RAM
- Agent finds a flag in
PrintFlagsFinaland recommends it - ~900 flags exist and almost all are for JVM support engineers - Agent recommends
-XX:+AggressiveHeap- hides a dozen settings, some now harmful - Agent omits other options when checking a default with
PrintFlagsFinal- flags affect each other