Method Handle
Table of Contents
1. Method Handle
1.1. Overview
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.invoke.MethodHandles; interface ITest { default public void dump() { System.out.println("ITest"); } } class Test implements ITest { public int x = 1; @Override public void dump() { System.out.println("Test"); } } public class playground { public static void main(String[] args) { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(void.class); try { MethodHandle handle = lookup.findVirtual(ITest.class, "dump", mt); handle.invoke(new Test()); handle.invoke(new ITest() {}); } catch (Throwable e) { System.out.println(e); } try { Test t = new Test(); MethodHandle handle = lookup.findSetter(Test.class, "x", int.class); handle.invoke(t, 100); System.out.println(t.x); } catch (Throwable e) { System.out.println(e); } try { Test t = new Test(); MethodHandle handle = lookup.findGetter(Test.class, "x", int.class); int tmp = (int)handle.invoke(t); System.out.println(tmp); } catch (Throwable e) { System.out.println(e); } // lookup.findStaticMethod // lookup.findstaticSetter // lookup.findstaticGetter // lookup.findConstructor // lookup.findSpecial } }
Test ITest 100 1
1.2. Some facts
- MethodHandle invoke 时仍然需要在运行时决定如何 dispatch: `handle.invoke(new Test())` 和 `handle.invoke(new ITest() {})` 会 dispatch 到不同的 method. handle 并不是 method 的 raw pointer
- lookup 时会作一些 access check, 而不像 reflection 那样在 invoke 时, 所以 MethodHandle 比 reflection 快一些
- MethodHandle 主要和 invokedynamic 有关, 因为 invokedynamic 要求 boostrap 函数返回一个 CallSite 对象, 其中的 MethodHandle 最终会被 invokedynamic 调用
1.3. Benchmark
import java.lang.reflect.Method; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.invoke.MethodHandles; interface ITest { public void dump(); } class Test implements ITest { public int x = 1; static int y = 1; public void dump() { this.x += 1; } public static void dumpStatic() { y += 1; } } public class playground { final static int ITERATIONS = 1000000; static void testMethodHandle() { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(void.class); MethodHandle handle = null; MethodHandle handle2 = null; try { handle = lookup.findVirtual(ITest.class, "dump", mt); handle2 = lookup.findStatic(Test.class, "dumpStatic", mt); } catch (Throwable e) { System.out.println(e); } long time = System.currentTimeMillis(); try { Test t = new Test(); for (int i = 0; i < ITERATIONS; i++) { if (handle != null) { handle.invoke(t); } } System.out.println("invoke dump from MethodHandle: " + (System.currentTimeMillis () - time)); time = System.currentTimeMillis(); } catch (Throwable e) {} try { for (int i = 0; i < ITERATIONS; i++) { if (handle != null) { handle2.invoke(); } } System.out.println("invoke dumpStatic from MethodHandle: " + (System.currentTimeMillis () - time)); } catch (Throwable e) {} } static void testReflection() { try { Method dumpMethod = Test.class.getDeclaredMethod("dump"); Method dumpStaticMethod = Test.class.getDeclaredMethod("dumpStatic"); Test t = new Test(); long time = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { dumpMethod.invoke(t); } System.out.println("invoke dump from reflection: " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { dumpStaticMethod.invoke(null); } System.out.println("invoke dumpStatic from reflection: " + (System.currentTimeMillis() - time)); } catch (Throwable e) { System.out.println(e); } } public static void main(String[] args) { testMethodHandle(); testReflection(); } }
maverick:/data/data # dalvikvm -Xint -cp classes.dex playground invoke dump from MethodHandle: 921 invoke dumpStatic from MethodHandle: 524 invoke dump from reflection: 985 invoke dumpStatic from reflection: 1009 1|maverick:/data/data # dalvikvm -cp classes.dex playground invoke dump from MethodHandle: 909 invoke dumpStatic from MethodHandle: 548 invoke dump from reflection: 1010 invoke dumpStatic from reflection: 990 /tmp/emacs-playgroud-java@udd934aa9cc4a5d> java -Xint playground invoke dump from MethodHandle: 533 invoke dumpStatic from MethodHandle: 359 invoke dump from reflection: 602 invoke dumpStatic from reflection: 550 /tmp/emacs-playgroud-java@udd934aa9cc4a5d> java playground invoke dump from MethodHandle: 10 invoke dumpStatic from MethodHandle: 7 invoke dump from reflection: 22 invoke dumpStatic from reflection: 21