Skip to content
Tech News
← Back to articles

Why is Arrays.fill 265 times slower on G1GC?

read original more articles
Why This Matters

This article highlights a surprising performance discrepancy in Java's G1GC versus ParallelGC when filling arrays, with G1GC being significantly slower. Understanding such JVM tuning issues is crucial for developers aiming to optimize Java application performance and avoid misdiagnosing bottlenecks. It underscores the importance of profiling and careful JVM configuration in high-performance Java applications.

Key Takeaways

[Java][JVM][Tuning][Profiling][G1][JIT] Why is Arrays.fill 265 times slower on G1GC?

Big fat warning

This article shows some JVM tuning using JVM flags. You should never use any JVM flags without knowing what consequences they may produce. Most of the flags used here are diagnostic ones, used to understand what is going on. Only one of them is worth considering on production, and I write about it at the very end.

The benchmark

It started with a benchmark that I expected to be boring. Fill two arrays with a reference, once on G1GC, once on ParallelGC:

package pl.ks.jmh ; import org.openjdk.jmh.annotations.* ; import java.util.Arrays ; import java.util.concurrent.TimeUnit ; @State ( Scope . Benchmark ) public class MyBenchmark { Object [] table = new Object [ 1024 * 1024 ]; Object [] table2 = new Object [ 1024 * 1024 ]; Object mark = new Object (); @Benchmark @Fork ( value = 1 , warmups = 1 , jvmArgsAppend = "-XX:+UseParallelGC" ) @OutputTimeUnit ( TimeUnit . MICROSECONDS ) @Warmup ( iterations = 1 ) @Measurement ( iterations = 2 ) @BenchmarkMode ( Mode . AverageTime ) public void parallelGC () { Arrays . fill ( table , mark ); Arrays . fill ( table2 , mark ); } @Benchmark @Fork ( value = 1 , warmups = 1 , jvmArgsAppend = "-XX:+UseG1GC" ) @OutputTimeUnit ( TimeUnit . MICROSECONDS ) @Warmup ( iterations = 1 ) @Measurement ( iterations = 2 ) @BenchmarkMode ( Mode . AverageTime ) public void g1GC () { Arrays . fill ( table , mark ); Arrays . fill ( table2 , mark ); } }

There is no allocation in these methods, so there are no GC cycles at all during the measurement. Whatever the difference is, it cannot be “G1 collects garbage slower”. And yet:

Benchmark Mode Cnt Score Units MyBenchmark.g1GC avgt 2 139019,174 us/op MyBenchmark.parallelGC avgt 2 525,537 us/op

139 milliseconds versus 0.5 millisecond. The same Java code, the same JDK, the same machine. G1 is 265 times slower.

This article is the story of chasing that number down to a single machine instruction, and then finding out that the instruction was only half of the answer.

... continue reading