为何MethodHandle调用返回Object的方法比Reflection慢?
问题:MethodHandle性能不如Reflection的原因?
我希望以最高性能的方式通过反射调用一个返回Object类型的方法。我分别使用Reflection和MethodHandle实现了该功能,原本预期MethodHandle性能更优,但实际测试显示它比Reflection慢约20%-40%。
以下是我的JMH基准测试代码:
import org.openjdk.jmh.annotations.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; 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 AccessorBenchmark { private static final Object[] EMPTY_ARGS = new Object[0]; private POJO source; private Method method; private MethodHandle methodHandle; private MethodHandle methodHandleModifiedReturnType; @Setup public void setup() throws ReflectiveOperationException { source = new POJO(); method = source.getClass().getDeclaredMethod("getNumber"); methodHandle = MethodHandles.lookup().unreflect(method); methodHandleModifiedReturnType = methodHandle.asType(methodHandle.type().changeReturnType(Number.class)); } @Benchmark public Number reflection() throws Throwable { return (Number) method.invoke(source, EMPTY_ARGS); } @Benchmark public Number methodHandle() throws Throwable { return (Number) methodHandle.invoke(source); } @Benchmark public Number methodHandleInvokeExact() throws Throwable { return (Number) methodHandleModifiedReturnType.invokeExact(source); } public class POJO { private final AtomicInteger counter = new AtomicInteger(); public AtomicInteger getNumber() { return counter; } } }
Java 17环境下的测试结果如下:
Benchmark Mode Cnt Score Error Units AccessorBenchmark.methodHandle avgt 1000 2.856 ± 0.004 ns/op AccessorBenchmark.methodHandleInvokeExact avgt 1000 2.359 ± 0.003 ns/op AccessorBenchmark.reflection avgt 1000 2.017 ± 0.002 ns/op
原因分析
JVM对反射的深度优化:从Java 7开始,JVM针对
Method.invoke做了多层优化。当同一个Method实例被调用足够多次后,JVM会生成专门的字节码片段并缓存,直接执行这个片段而非走通用反射逻辑,大幅降低了调用开销。尤其是你的测试场景是无参方法,这种优化的效果会更显著。MethodHandle调用的额外开销:
methodHandle.invoke()是通用调用方法,会自动处理类型适配、参数装箱拆箱等逻辑,这些额外操作带来了明显的性能损耗,这也是该测试项最慢的原因。- 虽然
invokeExact()是MethodHandle的高效调用方式,但你通过asType修改了返回类型(从AtomicInteger转为Number),这一步会强制插入类型转换的逻辑,抵消了invokeExact()的部分性能优势。如果直接使用原方法的返回类型调用invokeExact,性能会更接近甚至超过反射。
测试场景的特殊性:你的测试方法仅仅返回一个已初始化的
AtomicInteger实例,方法本身几乎没有业务逻辑,所有开销都集中在调用机制上。这种极端场景下,反射的缓存优化被最大化,而MethodHandle的类型适配开销被放大,导致了测试结果的反差。
验证建议
修改methodHandleInvokeExact基准测试方法,直接返回AtomicInteger并去掉asType的类型转换:
@Benchmark public AtomicInteger methodHandleInvokeExactRaw() throws Throwable { return (AtomicInteger) methodHandle.invokeExact(source); }
重新运行测试,这个测试项的性能应该会超过反射的测试结果,体现出MethodHandle的真正优势。
内容的提问来源于stack exchange,提问作者JackPGreen
相关产品推荐
相关产品推荐

