2015-06-01:已更新,以反映@JoeC对句柄为静态的另一种情况的评论。 还更新为最新的JMH,并在现代硬件上重新运行。结论几乎保持不变。
请进行适当的基准测试,对于JMH来说并不是很难。一旦这样做,答案就显而易见了。它还可以展示的正确用法
invokeExact(需要目标/源1.7进行编译和运行):
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)@Fork(3)@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)@State(Scope.Thread)public class MHOpto { private int value = 42; private static final Field static_reflective; private static final MethodHandle static_unreflect; private static final MethodHandle static_mh; private static Field reflective; private static MethodHandle unreflect; private static MethodHandle mh; // We would normally use @Setup, but we need to initialize "static final" fields here... static { try { reflective = MHOpto.class.getDeclaredField("value"); unreflect = MethodHandles.lookup().unreflectGetter(reflective); mh = MethodHandles.lookup().findGetter(MHOpto.class, "value", int.class); static_reflective = reflective; static_unreflect = unreflect; static_mh = mh; } catch (IllegalAccessException | NoSuchFieldException e) { throw new IllegalStateException(e); } } @Benchmark public int plain() { return value; } @Benchmark public int dynamic_reflect() throws InvocationTargetException, IllegalAccessException { return (int) reflective.get(this); } @Benchmark public int dynamic_unreflect_invoke() throws Throwable { return (int) unreflect.invoke(this); } @Benchmark public int dynamic_unreflect_invokeExact() throws Throwable { return (int) unreflect.invokeExact(this); } @Benchmark public int dynamic_mh_invoke() throws Throwable { return (int) mh.invoke(this); } @Benchmark public int dynamic_mh_invokeExact() throws Throwable { return (int) mh.invokeExact(this); } @Benchmark public int static_reflect() throws InvocationTargetException, IllegalAccessException { return (int) static_reflective.get(this); } @Benchmark public int static_unreflect_invoke() throws Throwable { return (int) static_unreflect.invoke(this); } @Benchmark public int static_unreflect_invokeExact() throws Throwable { return (int) static_unreflect.invokeExact(this); } @Benchmark public int static_mh_invoke() throws Throwable { return (int) static_mh.invoke(this); } @Benchmark public int static_mh_invokeExact() throws Throwable { return (int) static_mh.invokeExact(this); }}在1x4x2 i7-4790K,JDK 8u40,Linux x86_64上产生:
Benchmark Mode Cnt Score Error UnitsMHOpto.dynamic_mh_invoke avgt 25 4.393 ± 0.003 ns/opMHOpto.dynamic_mh_invokeExact avgt 25 4.394 ± 0.007 ns/opMHOpto.dynamic_reflect avgt 25 5.230 ± 0.020 ns/opMHOpto.dynamic_unreflect_invoke avgt 25 4.404 ± 0.023 ns/opMHOpto.dynamic_unreflect_invokeExact avgt 25 4.397 ± 0.014 ns/opMHOpto.plain avgt 25 1.858 ± 0.002 ns/opMHOpto.static_mh_invoke avgt 25 1.862 ± 0.015 ns/opMHOpto.static_mh_invokeExact avgt 25 1.859 ± 0.002 ns/opMHOpto.static_reflect avgt 25 4.274 ± 0.011 ns/opMHOpto.static_unreflect_invoke avgt 25 1.859 ± 0.002 ns/opMHOpto.static_unreflect_invokeExact avgt 25 1.858 ± 0.002 ns/op
…这表明在这种特定情况下,MH实际上比反射快得多(这是因为针对私有字段的访问检查是在查找时而不是在调用时完成的)。
dynamic_*情况模拟了当
MethodHandles和/或
Fields不是静态已知(例如从中拉出
Map<String,MethodHandle>或类似的东西)时的情况。相反,
static_*情况是调用程序是静态已知的。
请注意,在
dynamic_*某些情况下,反射性能与MethodHandles相当,这是因为反射在JDK
8中得到了进一步的优化(因为实际上,您不需要访问检查即可读取自己的字段),因此答案可能是“公正”切换到JDK 8;)
static_*案例甚至更快,因为
MethoHandles.invoke调用被积极地内联。这消除了MH情况下的部分类型检查。但是,在反思的情况下,仍然存在快速检查,因此,它落后了。



