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

Author: [email protected]
Date: 2019-08-23 Fri 00:00
Last updated: 2022-02-26 Sat 00:02

知识共享许可协议