反射、MethodHandle 与直接调用到底差多少?这篇先看 MethodHandle 的类型与访问规则,再用同一个 add 方法在四种调用方式下各调用 2000 万次(反射本身的开销已有专门的文章,MethodHandle 组合器在后面)。
句柄的类型 = (Calc,int,int)int(接收者变成第一个参数) invokeExact((int) …) = 5 invokeExact 但结果当 Object 用 → WrongMethodTypeException: handle's method type (Calc,int,int)int but found (Calc,int,int)Object invokeExact 传 Integer 而不是 int → WrongMethodTypeException: handle's method type (Calc,int,int)int but found (Calc,Integer,int)int invoke(自动装箱拆箱适配) = 5 bindTo(c) 后的类型 = (int,int)int,调用 bound.invoke(10, 20) = 30 greet 句柄 asType 成 (Object)Object 后调用 = hi ann Lookup 找另一个类 Other 的私有方法 secret → IllegalAccessException: no such method: Other.secret(int)int/invokeVirtual 方法不存在 → NoSuchMethodException: no such method: Main$Calc.nope()void/invokeVirtual privateLookupIn(Other.class, lookup) 之后找 secret 并调用 = 42
直接调用 预热后每次调用 0.31 ns(后 5 轮中位数) Method.invoke(缓存的 Method) 预热后每次调用 2.76 ns(后 5 轮中位数) static final MethodHandle.invokeExact 预热后每次调用 0.31 ns(后 5 轮中位数) 非 final 字段里的 MethodHandle.invokeExact 预热后每次调用 2.34 ns(后 5 轮中位数) static final MethodHandle.invoke 预热后每次调用 0.31 ns(后 5 轮中位数)