Skip to content

📈 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.