Java – performance advantages of static empty array instances
The usual approach is to extract the return value of an empty array of constants as static constants Like here:
public class NoopParser implements Parser { private static final String[] EMPTY_ARRAY = new String[0]; @Override public String[] supportedSchemas() { return EMPTY_ARRAY; } // ... }
Presumably, this is for performance reasons, because returning a new string [0] directly each time the method is called creates a new array object - but does it really?
I've always wondered if this really has a measurable performance advantage, or if it's just outdated folk wisdom Empty arrays are immutable Can VM not roll all empty string components into one? Can VM basically use the new string [0] for free?
Compare this exercise with returning an empty string: we are usually happy to write back ""; Instead of returning empty_ STRING;.
Solution
I benchmarked it with jmh:
private static final String[] EMPTY_STRING_ARRAY = new String[0]; @Benchmark public void testStatic(Blackhole blackhole) { blackhole.consume(EMPTY_STRING_ARRAY); } @Benchmark @Fork(jvmArgs = "-XX:-EliminateAllocations") public void testStaticEliminate(Blackhole blackhole) { blackhole.consume(EMPTY_STRING_ARRAY); } @Benchmark public void testNew(Blackhole blackhole) { blackhole.consume(new String[0]); } @Benchmark @Fork(jvmArgs = "-XX:-EliminateAllocations") public void testNewEliminate(Blackhole blackhole) { blackhole.consume(new String[0]); } @Benchmark public void noop(Blackhole blackhole) { }
Full source code.
Environment (see after Java - jar target / benchmarks.jar - F 1):
# JMH 1.11.2 (released 51 days ago) # VM version: JDK 1.7.0_75,VM 24.75-b04 # VM invoker: /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java # VM options: <none> # Warmup: 20 iterations,1 s each # Measurement: 20 iterations,1 s each # Timeout: 10 min per iteration # Threads: 1 thread,will synchronize iterations # Benchmark mode: Throughput,ops/time
Eliminatealocations is enabled by default (see Java - XX: printflagsfinal - version | grep eliminatealocations)
result:
Benchmark Mode Cnt score Error Units MyBenchmark.testNewEliminate thrpt 20 95912464.879 ± 3260948.335 ops/s MyBenchmark.testNew thrpt 20 103980230.952 ± 3772243.160 ops/s MyBenchmark.testStaticEliminate thrpt 20 206849985.523 ± 4920788.341 ops/s MyBenchmark.testStatic thrpt 20 219735906.550 ± 6162025.973 ops/s MyBenchmark.noop thrpt 20 1126421653.717 ± 8938999.666 ops/s
Using constants is almost twice as fast
Closing eliminateallocations slowed down a little