📈 Benchmark 003: Getting Field Values
📘 Preface
Reading from a field may appear costless, but the performance varies depending on how the field is accessed. Whether the field is accessed directly, via reflection, or through VarHandles (MethodHandles pre Java 9!), the JVM handles these operations differently.
🔬 Test Case
java
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class BM_003_GetField {
public static class TestClass {
static final TestClass INSTANCE = new TestClass();
static final Field STATIC_FIELD, FIELD;
static final VarHandle STATIC_FIELD_HANDLE, FIELD_HANDLE;
static {
try {
STATIC_FIELD = TestClass.class.getField("staticField");
STATIC_FIELD_HANDLE = MethodHandles.lookup().unreflectVarHandle(STATIC_FIELD);
FIELD = TestClass.class.getField("field");
FIELD_HANDLE = MethodHandles.lookup().unreflectVarHandle(FIELD);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static int staticField = 42;
public int field = 42;
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticField(Blackhole bh) {
bh.consume(TestClass.staticField);
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticFieldReflection(Blackhole bh) throws IllegalAccessException {
bh.consume((int) TestClass.STATIC_FIELD.get(null));
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticFieldVH(Blackhole bh) {
bh.consume((int) TestClass.STATIC_FIELD_HANDLE.get());
}
@Benchmark
@OperationsPerInvocation(1000)
public void field(Blackhole bh) {
bh.consume(TestClass.INSTANCE.field);
}
@Benchmark
@OperationsPerInvocation(1000)
public void fieldReflection(Blackhole bh) throws IllegalAccessException {
bh.consume((int) TestClass.FIELD.get(TestClass.INSTANCE));
}
@Benchmark
@OperationsPerInvocation(1000)
public void fieldVH(Blackhole bh) {
bh.consume((int) TestClass.FIELD_HANDLE.get(TestClass.INSTANCE));
}
}✅ Results
Benchmark Mode Cnt Score Error Units
BM_003_GetField.field avgt 5 0.001 ± 0.001 ns/op
BM_003_GetField.fieldReflection avgt 5 0.003 ± 0.001 ns/op
BM_003_GetField.fieldVH avgt 5 ≈ 10⁻³ ns/op
BM_003_GetField.staticField avgt 5 ≈ 10⁻³ ns/op
BM_003_GetField.staticFieldReflection avgt 5 0.002 ± 0.001 ns/op
BM_003_GetField.staticFieldVH avgt 5 0.001 ± 0.001 ns/op🔎 Findings
We can see that VarHandles, when stored in a static final variable can be inlined to basically the same as a field access. Reflection still lags behind a little.