我写了一个基准测试来测试java.lang.invoke.MethodHandlejava.lang.reflect.Method和方法的直接调用的性能。

我读到MethodHandle.invoke()的性能几乎与直接调用相同。但是我的测试结果显示了另一个:MethodHandle调用的速度大约比反射慢三倍。我怎么了可能是某些JIT优化的结果吗?

public class Main {
    public static final int COUNT = 100000000;
    static TestInstance test = new TestInstance();

    static void testInvokeDynamic() throws NoSuchMethodException, IllegalAccessException {
        int [] ar = new int[COUNT];

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(int.class);

        MethodHandle handle = lookup.findStatic(TestInstance.class, "publicStaticMethod", mt) ;

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)handle.invokeExact();
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("InvokeDynamic time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testDirect() {
        int [] ar = new int[COUNT];

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = TestInstance.publicStaticMethod();
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Direct call time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testReflection() throws NoSuchMethodException {
        int [] ar = new int[COUNT];

        Method method = test.getClass().getMethod("publicStaticMethod");

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)method.invoke(test);
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Reflection time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testReflectionAccessible() throws NoSuchMethodException {
        int [] ar = new int[COUNT];

        Method method = test.getClass().getMethod("publicStaticMethod");
        method.setAccessible(true);

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)method.invoke(test);
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Reflection accessible time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public static void main(String ... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InterruptedException {
        Thread.sleep(5000);

        Main.testDirect();
        Main.testInvokeDynamic();
        Main.testReflection();
        Main.testReflectionAccessible();

        System.out.println("\n___\n");

        System.gc();
        System.gc();

        Main.testDirect();
        Main.testInvokeDynamic();
        Main.testReflection();
        Main.testReflectionAccessible();
    }
}

环境: Java版本“1.7.0_11” Java SE运行时环境(内部版本1.7.0_11-b21)Java HotSpot(TM)64位服务器VM(内部版本23.6-b04,混合模式)OS-Windows 7 64

最佳答案

看起来@AlekseyShipilev间接引用了另一个查询来回答此问题。
在下面的链接
How can I improve performance of Field.set (perhap using MethodHandles)?
如果您通读,将会看到其他基准,这些基准显示出相似的发现。 JIT可能可以通过以下方式简单地优化直接调用:
根据以上发现,差异为:

  • MethodHandle.invoke =〜195ns
  • MethodHandle.invokeExact =〜10ns
  • 直接调用= 1.266ns

  • 所以-直接通话仍然会更快,但是MH会非常快。
    对于大多数用例,这应该足够了,并且肯定比旧的反射框架要快(顺便说一句,根据上面的发现,在Java8 vm下反射也要快得多)
    如果这种差异在您的系统中很重要,我建议您找到不同的模式而不是直接反射,这将支持直接调用。

    关于java - MethodHandle性能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15621434/

    10-13 04:58