📈 Benchmark 002: Setting Field Values
📘 Preface
Setting a field in Java seems like a trivial operation, but the method of access can introduce significant overhead... especially when reflection or through VarHandles (MethodHandles pre Java 9!) are used.
🔬 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_002_SetField {
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 = 0;
public int field = 0;
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticField(Blackhole bh) {
TestClass.staticField = 42;
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticFieldReflection() throws IllegalAccessException {
TestClass.STATIC_FIELD.setInt(null, 42);
}
@Benchmark
@OperationsPerInvocation(1000)
public void staticFieldVH() {
TestClass.STATIC_FIELD_HANDLE.set((int) 42);
}
@Benchmark
@OperationsPerInvocation(1000)
public void field() {
TestClass.INSTANCE.field = 42;
}
@Benchmark
@OperationsPerInvocation(1000)
public void fieldReflection() throws IllegalAccessException {
TestClass.FIELD.setInt(TestClass.INSTANCE, 42);
}
@Benchmark
@OperationsPerInvocation(1000)
public void fieldVH() {
TestClass.FIELD_HANDLE.set(TestClass.INSTANCE, 42);
}
}✅ Results
Benchmark Mode Cnt Score Error Units
BM_002_SetField.field avgt 5 ≈ 10⁻³ ns/op
BM_002_SetField.fieldReflection avgt 5 0.003 ± 0.001 ns/op
BM_002_SetField.fieldVH avgt 5 ≈ 10⁻³ ns/op
BM_002_SetField.staticField avgt 5 ≈ 10⁻³ ns/op
BM_002_SetField.staticFieldReflection avgt 5 0.003 ± 0.001 ns/op
BM_002_SetField.staticFieldVH avgt 5 ≈ 10⁻³ 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.