javaperformancereflectionmethodhandlevarhandle

Java poor performance with non-static `VarHandle`


I was investigating if replacing a java.lang.reflect.Field.get call with a VarHandle would improve performance, but instead it got slower.

From benchmarking, I can see that when the VarHandle is static, it outperforms Field.get (obviously) - but when it's not, it's about twice as slow.

For my use-case, it needs to generic across unknown classes - so not static - e.g. think about serialization.

Is there an alternative way to outperform Field.get?

Benchmark:

import org.openjdk.jmh.annotations.*;
import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 200, time = 10, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class ReflectiveFieldAccessBenchmark {
    static {
        try {
            field = ReflectiveFieldAccessBenchmark.class.getDeclaredField("count");
        } catch (final ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private static final Field field;
    private static final VarHandle staticVarHandle = getVarHandle();
    private final VarHandle nonStaticVarHandle = getVarHandle();

    private final Number count = new AtomicInteger(Integer.MAX_VALUE);

    private static VarHandle getVarHandle() {
        try {
            return MethodHandles.lookup().unreflectVarHandle(field);
        } catch (final ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    @Benchmark
    public Object reflection() throws Exception {
        return field.get(this);
    }

    @Benchmark
    public Object staticVarHandle() throws Exception {
        return staticVarHandle.get(this);
    }

    @Benchmark
    public Object nonStaticVarHandle() throws Exception {
        return nonStaticVarHandle.get(this);
    }
}

Output:

Benchmark                                                                         Mode   Cnt   Score    Error   Units
ReflectiveFieldAccessBenchmark.nonStaticVarHandle                                 avgt  1000   3.485 ±  0.005   ns/op
ReflectiveFieldAccessBenchmark.reflection                                         avgt  1000   2.027 ±  0.002   ns/op
ReflectiveFieldAccessBenchmark.staticVarHandle                                    avgt  1000   0.433 ±  0.002   ns/op

Solution

  • Is there an alternative way to outperform Field.get?

    The Lmbda library

    ToIntFunction<ReflectiveFieldAccessBenchmark> lambdaFactory = LambdaFactory.create(new LambdaType<ToIntFunction<ReflectiveFieldAccessBenchmark>>() {
            }, MethodHandles.lookup().unreflectGetter(field));
    
    lambdaFactoryBoxed.apply(this);
    
    Implementation ns/op
    Direct 0.32
    Reflection 1.92
    Lmbda 0.68