There are times when I'd happily trade more frequent GC pauses for a smaller per-process memory footprint. How do you find a reasonably small Xmx that doesn't lead to OutOfMemoryError exceptions?
There are times when I'd happily trade more frequent GC pauses for a smaller per-process memory footprint. How do you find a reasonably small Xmx that doesn't lead to OutOfMemoryError exceptions?
That's tough to figure out. In new versions of Java, I think 14+, if you use ZGC collector it will return unused memory to the OS. Memory options vary depending on collector, but new versions of ZGC support "soft max" heap size and uncommit. Together it might be close to what you're looking for https://malloc.se/blog/zgc-softmaxheapsize
I should mention the GC situation was worse until the last few years. Until ZGC and Shenandoah came around, Java still didn't collect frequently but when it did there were long pauses. This is what V8 and Go's collectors were designed to avoid. They have more overhead from collecting frequently, but low pauses. With the new Java collectors you get the best of both worlds.
You can set heap ratios and such for older collectors to decrease Java memory use with those, but IMO you're better off using ZGC and uncommit these days
Newer versions of ZGC support class unloading and have some other performance optimizations
Indeed. That fact obviates this option in most cases. You have to spend time tweaking obscure, unstable knobs (the X in Xmx means Oracle is free to alter its meaning at any time) and risk either a.) serious failures in production or b.) poor results because the conservative choices necessary to avoid 'a' achieved little improvement and you wasted your time. The real world for most enterprises is a vast heard of communicating components and toying with GC switches multiplied by N things is a nonstarter.
So while you're technically correct that excessive memory use by conventional JVMs is "not always true," in practice you are wrong. That reality comes with a real cost that appears on a real bill every single month.
Go's approach of minimal knobs leads to unfixable problems in production. Java gives more options to tune GC for your use case
I didn't bring up Go, but since you did the thing I see is that Go -- a much younger language -- is going places Java never has, or did so only haltingly. Caddy is a case in point. Here is Go taking on nginx, haproxy, Envoy, etc.
The people that once imagined using Java for such things have retired or moved on to other battles. No one seriously ponders attempting 'systems' tasks with Java any longer; that whole space was ceded to more efficient languages. My opinion is that Java's poor efficiency -- a big part of which is its excessive memory consumption -- is the reason for this.
That's my opinion. What I know for fact is that today, when people are making design decisions about new services and their deployment, Java is a problem; it is understood that anything implemented in Java is going to sort right to the top of the list of memory pigs in the cluster, and you can only afford so many of those.
This feels like G1GC erasure. I'll also say we've tried out ZGC and while pause times were low, it had a huge CPU overhead and the performance of our application was notably worse and we went back to G1GC. We're still on Java 11, so maybe we'll see some magic when we eventually try the newer versions.
Give it a try.
https://wiki.openjdk.java.net/display/shenandoah/Main
https://www.slideshare.net/jelastic/choosing-right-garbage-c...