These are all the options that have ever existed, including options that are or were available only in debug builds used during development and diagnostic options. There are still a few hundred non-diagnostic "product" flags at any one time, but most are intentionally undocumented (the list is compiled from the source code [1]) and are similar in spirit to compiler/linker configuration flags (only in Java, compilation and linking are done at runtime) and they're mostly concerned with various resource constants. It is very rare for most of them to ever be set manually, but if there's some unusual environment or condition, they can be helpful.
It's a result of Java being required to run on many different OS environments (Oracle, Redhat, Windows, RISC/ARM/x86), along with user constraints and also business requirements.
In a way you can use this list of JVM options to illustrate how successful Java has become, that everyone needs an option to get it to work how they like it.
As a Java dev, I have maybe used about 10-15 of them in my career.
The weirdest/funnest one I used was for an old Sun Microsystems Solaris server which ran iPlanet, for a Java EE service.
Since this shared resources with some other back of office systems, it was prone to run out of memory.
Luckily there was a JVM option to handle this!
-XX:OnOutOfMemoryError=""
It wasn't too important so we just used to trigger it to restart the whole machine, and it would come back to life. Sometimes we used to mess about and get it to send funny IRC messages like "Immah eaten all your bytez I ded now, please reboot me"
GC threads are generally often useful on multi-tenant systems or machines with many cores, as Java will default-size its thread pools according to the number of logical cores. If the server has 16 or more cores, that's very rarely something you want, especially if you run multiple JVMs on the same host.
Not JVM options, but these are often also good to tune:
You can get into difficulty with kubernetes here, as your jvm will detect all cores on the node but you may have set a resources limit on the pod/whatever, so it’ll assume it can spend more time doing stuff than it actually can, so often times it’s quite necessary to tune some things to prevent excessive switching etc.
Modern JVMs will detect orchestrator-set cgroup limits and size themselves accordingly. If you, for example, set a cpu limit for a pod to “1”, the JVM will size itself as if it was running on a single core machine.
Nah they fixed the JVM to be container aware some versions ago - I do remember dealing with this in early Java 8 days, think Java 10 is when it got fixed, and then it was backported to later releases of Java 8.
In what way is gofmt remotely comparable to a JVM?
In reality the number of options is significantly smaller than the 1843 you mentioned. The list contains boatloads of duplicates because they exist for multiple architectures. E.g. BackgroundCompilation is present on 8 lines on the OpenJDK 25 page: aarch64, arm, ppc, riscv, s390, x86 and twice more without an architecture.
The comparison with gofmt makes no sense. If Go had myriads of compiler implementations (the analogy being target environments for the JVM) that all had different performance characteristics and other behavioral differences depending on how the source code is formatted, you bet that gofmt would have a lot of options as well.
As a sysadmin, not developer, I hate Java almost as much as Windows. The error messages Java apps produce are like coded messages that you have to decipher.
I.E. Instead of " TLS Handshake failed" it will be something like "ERROR: PKIX failed". So now I have to figure out that PKIX is referring to PKI and it would make too much sense to provide the domain that failed. Instead I have to play the guessing game.
> 1843 options is too many. You could never even consider all of the possible combinations and interactions, let alone test them.
You can search for those that may concern you. Good old search or AI "search".
For example I recently did test the AOT compilation of Clojure (on top of the JVM) code using "Leyden". I used an abandoned Github project as a base but all the JVM parameters related to Leyden had changed names (!) and the procedure had to be adapted. I did it all (as a Dockerfile) in less than an hour with Sonnet 4.6 (complete with downloading/verifying the Leyden JVM, testing, taking notes about the project, testing on different machines, etc.).
These are not trivial calls to the "java" command: it involves a specific JVM and several JVM params that have to work fine together.
The goal was to load 80 000 Clojure/java classes (not my idea: the original project did that part) and see the results: 1.5 seconds to launch with the Leyden JVM (and correct params) vs 6 seconds for a regular launch (so a 75% gain). GraalVM is even faster but much more complicated/annoying to get right.
It can look overwhelming but I'd say all these parameters are there for a reason and you only need a few of them. But when you need them, you need them.
P.S: unrelated to TFA and as a bonus for the "Java is slow crowd":
time java -jar hello/hello.jar
Hello, World!
real 0m0.040s
And that's without any Leyden/GraalVM trick. For Clojure the "slow" startup times are due to each Clojure function being transformed into one Java .class each and there are many Clojure functions. Hence the test with 80 000 Clojure functions from the project I reused: https://github.com/jarppe/clojure-app-startup-time-test
(but it's not maintained, won't work as if with the latest Leyden JVM)
Thank god you have no say in where modern tooling is heading, at the creator of the site, absolute right choice to leave it up to the user to chose all options.
In the age of LLMs coupled with open source software, option count is unlimited. I fork FOSS projects and modify them for my own use all the time. Sometimes, with an agent, doing so is even easier than finding the "right" knob.
Wasn't it Joel Spolsky who said every option is a cop out? Or maybe Steve Yegge? I forget. It's something I agree with. I often have this thought when going through the options of something conceptually fairly simple: "who is this for? who actually uses this option?"
I kinda feel the same way with C/C++ warnings. Different code bases decide if different warnings are errors. That was a mistake (IMHO).
The other thought I have scanning these options is how many are related to GC. I kinda think GC is a bit of a false economy. It's just hiding the complexity. I wonder if it would've been better to push GC to be pluggable rather than relying on a host of options, a bit like TCP congestion management. I mean there are /proc parameters for that in Linux, for example, but it's also segregated (eg using BRR).
At the end of the day, none of this really matters. As in, the JVM is mature and I think generally respected.
I appreciate the philosophy behind Go as much as anyone, but this comment sounds tone deaf, and indicative of a certain immaturity.
Many of these flags are the equivalent of GODEBUG. GODEBUG does not have 1843 options, but do you know how many it has? Have you ever used GODEBUG? (I'm guessing no, hence, immaturity) And if not, do you think it should just be dropped because you never needed it?
His other project "Byte Me", along with judicious javap usage, has been super useful for me learning JVM bytecode so I could make a machine learning model compiler for the JVM (basically compile your ML models as native code; ONNX, tree ensembles, regressors, classifiers, etc as native JVM classes with no massive runtime needed)
This is going to come very handy for development of CodeBrew, my Java IDE for iPhone/iPad. It runs a full OpenJ9 JVM under the hood, and I had to do a bunch off massaging with the options to get it to run properly. I wish I had known this page sooner!
Those button at the top link to different domains altogether, but present the same page. So it is one page with multiple domains, instead of one domain with multiple pages.
(I know many conflict and there is not a shell buffer long enough to handle all that)
Kidding aside, I actually said "ugh, seriously" when I saw that there were literally thousands of options. Is there a public program with more options?
All of that configuration and it will always be less efficient than Rust, or even Golang.
This is why lots of engineers waste time fiddling with options to tune the JVM and still require hundreds of replicated micro-services to "scale" their backends and losing money on AWS and when they will never admit the issue is the technology they have chosen (Java) and why AWS loves their customers using inefficient and expensive technologies.
Even after that, both Go and Rust continue to run rings around the JVM no matter the combination of options.
120 comments
An interface like above to sort things would probably be quite helpful as well.
[0] https://peter.sh/experiments/chromium-command-line-switches/
archived on 2026-04-12: https://web.archive.org/web/20260412085953/https://peter.sh/...
Same as, say, ANTLR generates code to parse various texts to AST.
I have really come to appreciate modern opinionated tooling like gofmt, that does not come with hundreds to thousands of knobs.
[1]: https://github.com/openjdk/jdk/blob/master/src/hotspot/share...
In a way you can use this list of JVM options to illustrate how successful Java has become, that everyone needs an option to get it to work how they like it.
As a Java dev, I have maybe used about 10-15 of them in my career.
The weirdest/funnest one I used was for an old Sun Microsystems Solaris server which ran iPlanet, for a Java EE service.
Since this shared resources with some other back of office systems, it was prone to run out of memory.
Luckily there was a JVM option to handle this!
-XX:OnOutOfMemoryError=""
It wasn't too important so we just used to trigger it to restart the whole machine, and it would come back to life. Sometimes we used to mess about and get it to send funny IRC messages like "Immah eaten all your bytez I ded now, please reboot me"
I suggest most people never touch almost any other options. (Flight recording and heap dumps being the exception).
Not JVM options, but these are often also good to tune:
In my experience this often both saves memory and improves performance.> As a Java dev, I have maybe used about 10-15 of them in my career.
So do we really need multiple thousand? Having all of them also makes finding the few you actually need much more difficult.
In reality the number of options is significantly smaller than the 1843 you mentioned. The list contains boatloads of duplicates because they exist for multiple architectures. E.g. BackgroundCompilation is present on 8 lines on the OpenJDK 25 page: aarch64, arm, ppc, riscv, s390, x86 and twice more without an architecture.
The JVM is like an operating system. A better comparison would be Linux kernel parameters: https://www.kernel.org/doc/html/latest/admin-guide/kernel-pa...
> You could never even consider all of the possible combinations and interactions, let alone test them.
Nobody has ever tested all possible inputs to 64 bit multiplication either. You can sample from the space.
I.E. Instead of " TLS Handshake failed" it will be something like "ERROR: PKIX failed". So now I have to figure out that PKIX is referring to PKI and it would make too much sense to provide the domain that failed. Instead I have to play the guessing game.
> 1843 options is too many. You could never even consider all of the possible combinations and interactions, let alone test them.
You can search for those that may concern you. Good old search or AI "search".
For example I recently did test the AOT compilation of Clojure (on top of the JVM) code using "Leyden". I used an abandoned Github project as a base but all the JVM parameters related to Leyden had changed names (!) and the procedure had to be adapted. I did it all (as a Dockerfile) in less than an hour with Sonnet 4.6 (complete with downloading/verifying the Leyden JVM, testing, taking notes about the project, testing on different machines, etc.).
These are not trivial calls to the "java" command: it involves a specific JVM and several JVM params that have to work fine together.
The goal was to load 80 000 Clojure/java classes (not my idea: the original project did that part) and see the results: 1.5 seconds to launch with the Leyden JVM (and correct params) vs 6 seconds for a regular launch (so a 75% gain). GraalVM is even faster but much more complicated/annoying to get right.
It can look overwhelming but I'd say all these parameters are there for a reason and you only need a few of them. But when you need them, you need them.
P.S: unrelated to TFA and as a bonus for the "Java is slow crowd":
And that's without any Leyden/GraalVM trick. For Clojure the "slow" startup times are due to each Clojure function being transformed into one Java .class each and there are many Clojure functions. Hence the test with 80 000 Clojure functions from the project I reused: https://github.com/jarppe/clojure-app-startup-time-test (but it's not maintained, won't work as if with the latest Leyden JVM)Zillions of options. Some important, some not
I kinda feel the same way with C/C++ warnings. Different code bases decide if different warnings are errors. That was a mistake (IMHO).
The other thought I have scanning these options is how many are related to GC. I kinda think GC is a bit of a false economy. It's just hiding the complexity. I wonder if it would've been better to push GC to be pluggable rather than relying on a host of options, a bit like TCP congestion management. I mean there are /proc parameters for that in Linux, for example, but it's also segregated (eg using BRR).
At the end of the day, none of this really matters. As in, the JVM is mature and I think generally respected.
Many of these flags are the equivalent of GODEBUG. GODEBUG does not have 1843 options, but do you know how many it has? Have you ever used GODEBUG? (I'm guessing no, hence, immaturity) And if not, do you think it should just be dropped because you never needed it?
still in the works, but its here for those interested: Petrify: https://github.com/exabrial/petrify
For anyone intered, here's the app:
https://apps.apple.com/app/apple-store/id6475267297?pt=11914...
But here it is: JVM is a modern cathedral.
(I know many conflict and there is not a shell buffer long enough to handle all that)
Kidding aside, I actually said "ugh, seriously" when I saw that there were literally thousands of options. Is there a public program with more options?
This is why lots of engineers waste time fiddling with options to tune the JVM and still require hundreds of replicated micro-services to "scale" their backends and losing money on AWS and when they will never admit the issue is the technology they have chosen (Java) and why AWS loves their customers using inefficient and expensive technologies.
Even after that, both Go and Rust continue to run rings around the JVM no matter the combination of options.