I'm genuinely curious about the effect, but I simply don't have patience for the AI writing. Can anyone give an actual non-garbage explanation with some respect for the reader? Slightly less annoying summary from ChatGPT free: https://chatgpt.com/share/6a9ac7a3-15a0-83eb-8c2a-6f72cd9beb... . Caveat emptor: it makes high level sense, but I haven’t thought about it in detail.
Why is Arrays.fill 265 times slower on G1GC?
21–30 of 33 posts
Re: Why is Arrays.fill 265 times slower on G1GC?
#22I'm genuinely curious about the effect, but I simply don't have patience for the AI writing. Can anyone give an actual non-garbage explanation with some respect for the reader? Slightly less annoying summary from ChatGPT free: https://chatgpt.com/share/6a9ac7a3-15a0-83eb-8c2a-6f72cd9beb... . Caveat emptor: it makes high level sense, but I haven’t thought about it in detail.
Re: Why is Arrays.fill 265 times slower on G1GC?
#23I read through it. Not bad. In fact, pretty good. It is about something that they actually did. tl;dr: In JDk 25, filling a large array of references with objects living in a different heap region is extremely slow when using G1 GC as opposed to parallel GC. Solution: Move to Java 26. Or increase G1HeapRegionSize. Details: They used Amazon Corretto as JDK. When Arrays.fill() was called with UseG1GC and UseParallelGC,…
Re: Why is Arrays.fill 265 times slower on G1GC?
#24There are two practical lessons here: 1. Upgrade your JDK for the best performance (as the article says, the slowdown is gone in JDK 26). 2. Don't try to help the GC by pooling objects. Mutating old objects can be expensive, while allocating new ones is cheap (at least for objects that don't do some exceptionally expensive initialisation).
Re: Why is Arrays.fill 265 times slower on G1GC?
#25There are two practical lessons here: 1. Upgrade your JDK for the best performance (as the article says, the slowdown is gone in JDK 26). 2. Don't try to help the GC by pooling objects. Mutating old objects can be expensive, while allocating new ones is cheap (at least for objects that don't do some exceptionally expensive initialisation).
Honestly, I don't really understand why G1 is being pushed so hard. The parallel collector is a perfectly fine collector, particularly for smaller heaps. Even the serial collector isn't bad for things like a containerized environment, yet G1 replaces it by default now [1]. It's not a bad algorithm, but especially when you start talking about sub 2G environments I've not seen a situation where the parallel and serial…
Re: Why is Arrays.fill 265 times slower on G1GC?
#26There are two practical lessons here: 1. Upgrade your JDK for the best performance (as the article says, the slowdown is gone in JDK 26). 2. Don't try to help the GC by pooling objects. Mutating old objects can be expensive, while allocating new ones is cheap (at least for objects that don't do some exceptionally expensive initialisation).
Object pooling still has its place, but like any optimization it needs to be based on benchmarks and shouldn't be done haphazardly. Blindly pooling objects will lead to regressions and resource contention more often than improvements. There are also middle ground options, like pooling objects but giving the pool a lifecycle that is tied to a request.
In particular, the JDK's GCs are heavily optimised for short-lived objects with high allocation rates. What you want to avoid is temporary data finding itself in the old gen. The newer GCs may dynamically size the young generation to match your program's natural meaning of "short lived", but the longer an object lives, the higher the chances it ends up in the old gen. You want only objects that stick around for a very long time (and have a low allocation rate) to end up in old gen. If you just write naive code, chances are things will work out well. Once you start being clever, you're taking a risk.
So if you're willing to profile your program, with a workload that's representative of production workload (a microbenchmark is useless) on every runtime version and potentially change your "manual optimisation" every six months, you can try. But if not, the advice for the best performance over time is to rely on the platform and let it do its thing. The reason is that the JVM is continuously being optimised for "normal programs". If you're doing anything too clever, you may find that in a future release, your code is making things worse because the optimisations that target normal code don't help your code (or could even treat it as unusual and have it hit slow paths).
I once spoke to a company that were very proud in getting something like a 10% improvement over naive code in Java 8, thanks to some hand optimisation they worked a lot on, only to discover that it caused a 15% regression compared to doing nothing special on JDK 11.
Re: Why is Arrays.fill 265 times slower on G1GC?
#27Earlier quoted context omitted.
Honestly, I don't really understand why G1 is being pushed so hard. The parallel collector is a perfectly fine collector, particularly for smaller heaps. Even the serial collector isn't bad for things like a containerized environment, yet G1 replaces it by default now [1]. It's not a bad algorithm, but especially when you start talking about sub 2G environments I've not seen a situation where the parallel and serial…
If anything, I think it's not unlikely that ZGC will become the default at some point, as it matures. It's hard to beat Parallel on batch workloads, although G1 is getting there. ZGC is unparalleled for low-latency (GC pauses are just gone). G1 is intended to offer a compromise that could be a reasonable default.
I just have a problem with G1 because in my experience, the best place for it is fairly large heaps. Get something sub 2 or even 10G, especially if you have a few cores to offer, and the parallel and often even the serial collector will give G1 latency even on major collections with superior throughput and overhead.
Re: Why is Arrays.fill 265 times slower on G1GC?
#28Earlier quoted context omitted.
Object pooling still has its place, but like any optimization it needs to be based on benchmarks and shouldn't be done haphazardly. Blindly pooling objects will lead to regressions and resource contention more often than improvements. There are also middle ground options, like pooling objects but giving the pool a lifecycle that is tied to a request.
The problem is that 1. it's not easy to beat the JDK's GCs at memory management (assuming you've picked the right GC for your workload) especially as they keep getting better and better, and 2. how a pool behaves relative to the GC depends greatly on the GC algorithm (e.g. the same pool could help a bit with, say, Parallel GC, and hurt significantly with G1 or ZGC), and the different GC algorithms also tend to change…
Removing allocation pressure can also have effects on other parts of the system, but that is anything but trivial to measure or reason about.
Re: Why is Arrays.fill 265 times slower on G1GC?
#29Earlier quoted context omitted.
Honestly, I don't really understand why G1 is being pushed so hard. The parallel collector is a perfectly fine collector, particularly for smaller heaps. Even the serial collector isn't bad for things like a containerized environment, yet G1 replaces it by default now [1]. It's not a bad algorithm, but especially when you start talking about sub 2G environments I've not seen a situation where the parallel and serial…
If anything, I think it's not unlikely that ZGC will become the default at some point, as it matures. It's hard to beat Parallel on batch workloads, although G1 is getting there. ZGC is unparalleled for low-latency (GC pauses are just gone). G1 is intended to offer a compromise that could be a reasonable default.
In the modern enterprise Java world (that I've been exposed to) it's very common to have a mandate that all components deploy a minimum of N instances across X regions for resiliency. By design that almost always means you're deploying at least 2x more compute than you strictly need, so the top priority is generally minimizing per-instance overhead to minimize cloud spend.
For example, the default templates at my current company deploy something like 0.25-0.5 vCPU per instance, and therin lies the rub. ZGC performance is _catastrophically_ bad with 1s, because more time was being spent on "background" GC than actually servicing requests.
Hope that didn't come off adversarial. I just find GC fascinating, and ended up spending a bunch of time working with the team that owns those defaults to draft general recommendations. TLDR is that when in doubt don't specify/let the JVM pick for you, and don't be surprised if it picks serial :)
Re: Why is Arrays.fill 265 times slower on G1GC?
#30There are two practical lessons here: 1. Upgrade your JDK for the best performance (as the article says, the slowdown is gone in JDK 26). 2. Don't try to help the GC by pooling objects. Mutating old objects can be expensive, while allocating new ones is cheap (at least for objects that don't do some exceptionally expensive initialisation).