[HN Gopher] JVM Options Explorer
___________________________________________________________________
JVM Options Explorer
Author : 0x54MUR41
Score : 164 points
Date : 2026-04-12 10:29 UTC (12 hours ago)
(HTM) web link (chriswhocodes.com)
(TXT) w3m dump (chriswhocodes.com)
| Hendrikto wrote:
| 1843 options is too many. You could never even consider all of
| the possible combinations and interactions, let alone test them.
|
| I have really come to appreciate modern opinionated tooling like
| gofmt, that does not come with hundreds to thousands of knobs.
| tezza wrote:
| How is this different to system tuning parameters in Linux
| /proc, FreeBsd, Windows Registry, Firefox about:config,
| sockopt, ioctl, postgres?
|
| Zillions of options. Some important, some not
| mzi wrote:
| One of my nerd-quizzes I hade at interviews before was "what
| letters in what case are NOT flags to GNU ls".
| eru wrote:
| The answer is 'man ls'. And: 'almost all letters of unicode'.
| deepsun wrote:
| Just because you have more features and ways to use them. Say I
| like to use a different garbage collector for a tool.
| RadiozRadioz wrote:
| I don't think modernity is a noteworthy factor as to whether
| tooling is opinionated.
| elric wrote:
| 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.
| avianlyric wrote:
| gofmt isn't really comparable to the JVM, but it is a really
| strong expression of the opinionated tooling GoLang has.
|
| While gofmt is "just" a formatting tool. The interesting part
| is that go code that doesn't follow the go formatting
| standard is rejected by the go compiler. So not only does
| gofmt not have knobs, you can't even fork it to add knobs,
| because the rest of the go ecosystem will outright reject
| code formatted in any other way.
|
| It's a rather extreme approach to opinionated tooling. But
| you can't argue with the results, nobody writing go on any
| project ever worries about code formatting.
| kfuse wrote:
| They do worry, they just can't do anything about it. Like
| the fact that error handling code takes at least three
| lines no matter how trivial it is. I'm sure error handling
| would not be critisized nearly as much if it didn't consume
| so much vertical space and could fit in one line, which go
| compiler does allow.
| elric wrote:
| That's all well and good, but entirely irrelevant to the
| number of options a JVM should reasonably have.
| bram98 wrote:
| .
| parsd wrote:
| I don't believe the Go compiler would reject unformatted
| code. The compiler has its own set of rules for what it
| views as syntactically correct code, but these rules have
| nothing to do with gofmt's formatting rules.
|
| For example, it's the compiler and not gofmt that dictates
| that you must write a curly brace only on the same line of
| an "if" statement. If you put it on the next line, you
| don't have unformatted code - you have a syntax error.
|
| However, the compiler doesn't care if you have too much
| whitespace between tokens or if you write your slice like
| []int{1, 2,3,4}, but gofmt does.
|
| We could say the rules of the compiler and gofmt don't even
| overlap.
| tomaytotomato wrote:
| 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="<run command>"
|
| 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"
| nkzd wrote:
| Which JVM options do you use the most?
| cogman10 wrote:
| Heap size, GC algorithm.
|
| I suggest most people never touch almost any other options.
| (Flight recording and heap dumps being the exception).
| marginalia_nu wrote:
| 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:
| -Djdk.virtualThreadScheduler.parallelism
| -Djdk.virtualThreadScheduler.maxPoolSize
| -Djava.util.concurrent.ForkJoinPool.common.parallelism
|
| In my experience this often both saves memory and
| improves performance.
| cyberpunk wrote:
| 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.
| dpratt wrote:
| 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.
| vips7L wrote:
| Afaik this was fixed a long time ago.
| Hendrikto wrote:
| > 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.
| KronisLV wrote:
| > So do we really need multiple thousand?
|
| Assuming that you don't need 99.9% of them (they should
| have sane defaults that you never have to change or even
| learn that they exist or what they are) until that super
| rare case when one will save your hide, I'd lean towards
| yes.
|
| In other words, they might as well be an escape hatch of
| sorts, that goes untouched most of the time, but is there
| for a reason.
|
| > Having all of them also makes finding the few you
| actually need much more difficult.
|
| This is a good point! I'd expect the most commonly changed
| ones (e.g. memory allocation and thread pools) to be
| decently well documented, on the web and in LLM training
| data sets. Reading the raw docs will read like noise,
| however.
| nradov wrote:
| Did you RTFM? It's not difficult to find the few you
| actually need.
| eru wrote:
| > 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.
| pixl97 wrote:
| Eh that sounds a bit different to me, multiplication should
| be roughly the same operator on each test, these are wildly
| different functions.
| deepsun wrote:
| You forgot about NaNs (all of them), infinities and
| positive/negative zeros. Tests warranted.
| MaxBarraclough wrote:
| Don't forget the Intel floating-point division bug from
| the 90s.
|
| https://en.wikipedia.org/wiki/Pentium_FDIV_bug
| Geezus_42 wrote:
| 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 "<DOMAIN> 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.
| deepsun wrote:
| I hate when tools only produce generic "TLS Handshake failed"
| instead of saying why exactly it failed, where is the
| problem.
| thunky wrote:
| Sounds like you'd both be happy if the tool produced both.
| appplication wrote:
| This is why stack traces exist. But I agree Java seems to
| not really have a culture of "make the error message
| helpful", but instead preferring "make the error message
| minimal and factual".
|
| For what it's worth, the rise of helpful error messages
| seems to be a relatively new phenomenon the last few
| years.
| Geezus_42 wrote:
| Sounds to me that deepsun and I are in agreement that an
| error message should tell you what the actual error was.
|
| I.E. ERROR: TLS handshake failed: <DOMAIN> certificate
| chain unverified
| kitd wrote:
| This is the kind of scenario that is served better by
| Go/C-style error values than exceptions. Error values
| facilitate and encourage you to log what you were doing at
| the precise point when an error occurs. Doing the same with
| exceptions idiomatically often requires an exception
| hierarchy or copious amounts of separate try/catches.
|
| The difference really becomes apparent when trying to debug
| a customer's problem at 3am (IME).
| well_ackshually wrote:
| So your issue isn't with Java, just with shit error messages
| and devs clearing the exception stack.
| pron wrote:
| 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.
|
| [1]:
| https://github.com/openjdk/jdk/blob/master/src/hotspot/share...
| quotemstr wrote:
| 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.
| fHr wrote:
| 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.
| TacticalCoder wrote:
| > 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)
| vips7L wrote:
| I could be missing it because I'm not that familiar with bb,
| but looking at your repository it doesn't look like you're
| using any feature that was actually shipped with project
| Leyden. It looks like you're just using AppCDS which has been
| around for a long time.
| nromiun wrote:
| That test does not mean anything. I can also spin up a large
| LLM on my 5090 and say these models are ready for on device
| deployment now. However that would not be true for most
| people. You should test a Golang hello world binary as well.
| I bet it will take less than 40 milliseconds.
| jmyeet wrote:
| 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.
| vips7L wrote:
| The GC is pluggable, that's why you have so many to choose
| from depending on your work load. You rarely if ever have to
| touch those options. In the last 10 years all of my apps,
| since I run on a modern version of Java, only ever set max
| heap size and soon that will (finally) be figured out
| automatically: https://openjdk.org/jeps/8359211
| izacus wrote:
| Joe Spolsky also never created anything as popular and widely
| deployed as Java. It's easy to bloviate about pure software
| when it doesn't need to literally run the whole world as you
| know it.
| toyg wrote:
| Er. IIRC, Spolsky was involved in creating VBA for Excel.
| Which was arguably orders of magnitude more popular (and
| still more widely deployed and world-supporting) than Java.
| izacus wrote:
| In the 90s, as a program manager. And it's a big
| difference between building an application and a platform
| (I've done both) when it comes to API design.
| pjmlp wrote:
| Have you ever seen how many GCC has for plain old C?
| Hendrikto wrote:
| Yeah, that's a mistake too, and a big reason for why
| compiling C projects is such a pain.
|
| Notice how I did not compare to C, but modern alternatives.
| pjmlp wrote:
| Gccgo and tinygo do exist, with enough parameters.
| pron wrote:
| I'll grant you that Go is extremely opinionated; that's its
| shtick. But it's an old language that started out with a
| 1970s design as a statement by its creators against modern
| programming languages. From its langnauge design, through
| its compiler, to its GC algorithm, it is intentionally
| retro (Java retired its Go-like GC five years ago because
| the algorithm was too antiquated). It may suit your taste
| and I'm not suggesting that it's bad, but modern it is not.
| layer8 wrote:
| 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.
|
| 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...
| guusbosman wrote:
| There is a 2nd edition now of the Optimizing Java book you are
| referring to on your site.
| grodriguez100 wrote:
| He probably knows, since he is one of the authors.
| coolius wrote:
| 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!
|
| For anyone intered, here's the app:
|
| https://apps.apple.com/app/apple-store/id6475267297?pt=11914...
| rvz wrote:
| 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.
| cleverfoo wrote:
| Sure, for a very narrow definition of _efficiency_. There's
| plenty to complain in terms of the JVM and Java but
| performance, as in units of work per dollar spent, is not one
| of them - JITs just have too many opportunities for optimizing
| generated code.
| deepsun wrote:
| All of that tooling and Rust will always be less efficient than
| Assembler.
| metaltyphoon wrote:
| I... didn't think this makes sense :)
| msla wrote:
| It makes perfect sense: Rust compilers will never beat a
| human at scheduling every single opcode perfectly based on
| the deepest microarchitectural analysis short of decapping
| the chip and breaking out the ol' electron microscope.
| Whether it's worthwhile to be that efficient over a whole
| program, as opposed to a preternaturally tight compute
| kernel, is definitely questionable.
| fHr wrote:
| I was a diehard java fanboy but using Rust in the last 5 years
| more and more I have to agree, but sadly huge Java corporate
| codebases keep my bills paid still, so I have to deal with it.
| It is what it is. Also agree the pipeline etc. they love all
| the waste of the compute in their pocket.
| contraposit wrote:
| I hope AI will make automated translation of such legacy
| codebases into any favourite langauge possible in future.
| Fingers crossed.
| newsoftheday wrote:
| It sounds like you're not into Java. Perhaps consider
| switching languages to make room for people who are.
| deepsun wrote:
| I code on both and they are just for different purposes. E.g.
| I think it's madness to develop desktop apps in Rust.
|
| Development velocity is way greater in Java.
| arein3 wrote:
| Yeah doubt that
|
| Recently I had a python friend use the most balls to the wall
| python backend, he couldnt beleive java was faster, but the
| numbers werent lying. We did 1 billion iterations of adding a
| float, took a few seconds in java.
| well_ackshually wrote:
| That's a nice source, from where up your ass did you find it ?
|
| Go's GC is absolutely awful and leads to nondeterministic
| pauses and catastrophic latency spikes, especially when the
| memory pressure and capacity is high. Throw the go GC against a
| 256GB heap, see how well it survives.
|
| Technologies have strong and weak points. Go's strong points
| are small, targeted pieces of software and having 66% of a
| binary basically be if err != nil return err. Rust's strong
| points are that you get to have the symbol<():soup<_, |_| of {
| c++ }>> while not saying you're writing c++ and feeling really
| smug when you say that you only needed to use 5 Arc<Mutex<T>>
| and rewrote your entire software three times but at least it
| runs almost as fast as some shitty C that does fgets() in the
| middle of a hot loop. Java lets you spawn spring boot and
| instantiate a string through reflection because why not.
|
| I promise you, I can write allocation heavy
| FizzBuzzEnterpriseFactoryFactories in Rust too.
| motoboi wrote:
| People say we don't build cathedrals anymore.
|
| But here it is: JVM is a modern cathedral.
| well_ackshually wrote:
| Multiple generations of builders working together on a grand
| plan, constantly interrupted by multiple generations of
| ~~kings~~ multibillion dollar corporations to please add ~~a
| grand mural remembering his great deeds~~ yet another flag to
| control exactly the timing of GC pauses because it turns out
| our server can only do GC between 3 and 3:30 AM.
| exabrial wrote:
| 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)
|
| still in the works, but its here for those interested: Petrify:
| https://github.com/exabrial/petrify
| deepsun wrote:
| Why not compiling it to Java source code (not bytecode)? Users
| would use their own Java compiler then.
|
| Same as, say, ANTLR generates code to parse various texts to
| AST.
| molticrystal wrote:
| Chrome has 1496 [0] known options as of today, maybe after a few
| more pushes they'll catch up to the 1843 of JVM.
|
| An interface like above to sort things would probably be quite
| helpful as well.
|
| [0] https://peter.sh/experiments/chromium-command-line-switches/
| deepsun wrote:
| Why not compiling it to Java source code (not bytecode)? Users
| would use their own Java compiler then.
|
| Same as, say, ANTLR generates code to parse various texts to
| AST.
| zkmon wrote:
| 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.
| scrame wrote:
| OK, now make them all run at once!
|
| (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?
___________________________________________________________________
(page generated 2026-04-12 23:00 UTC)