java lambda之方法句柄&invokedynamic指令

Posted master-dragon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java lambda之方法句柄&invokedynamic指令相关的知识,希望对你有一定的参考价值。

目录

Java7 MethodHandle

参考文档:https://www.javacodegeeks.com/2011/03/glimpse-at-java-7-methodhandle-and-its.html

Due to Java’s Reflection API we have been able to inspect and alter program execution at runtime. In particular, we can observe interfaces/classes/methods and fields at runtime without knowing their names at compile time.

JDK 7 introduces a new player to this dynamic/runtime inspection, the method handle (i.e. a subclass of the abstract class java.dyn.MethodHandle). Method handles gives us unrestricted capabilities for calling non-public methods, e.g. it can be formed on a non-public method by a class that can access it. Compared to using the Reflection API, access checking is performed when the method handle is created as opposed to every time the method is called.

MethodHandle和反射一样也能够实现在运行期间访问私有方法,且更加高效。可以看成是:持有某个具体方法的指针,然后就能直接调用该句柄所引用的底层方法。与使用Reflection API相比,在创建方法句柄时(而不是在每次调用该方法时)执行访问检查。

例子代码:


import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


class MethodAccessExampleWithArgs
    private final int i;

    public MethodAccessExampleWithArgs(int i_) 
        i = i_;
    

    private void bar(int j, String msg) 
        System.out.println("Private Method \\'bar\\' successfully accessed : "
                + i + ", " + j + " : " + msg + "!");
    

    // Using Reflection,使用反射
    public static Method makeMethod() 
        Method meth = null;
        try 
            Class[] argTypes = new Class[]  int.class, String.class ;
            meth = MethodAccessExampleWithArgs.class.getDeclaredMethod("bar",
                    argTypes);
            meth.setAccessible(true);
         catch (IllegalArgumentException e) 
            e.printStackTrace();
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (SecurityException e) 
            e.printStackTrace();
        
        return meth;
    

    // Using method handles, 使用 MethodHandle
    public static MethodHandle makeMethodhandle() 
        MethodHandle mh = null;
        MethodType desc = MethodType.methodType(void.class, int.class,
                String.class);
        try 
            mh = MethodHandles.lookup().findVirtual(
                    MethodAccessExampleWithArgs.class, "bar", desc);
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (IllegalAccessException e) 
            e.printStackTrace();
        
        System.out.println("mh=" + mh);

        return mh;
    


/**
 * @author dingqi on 2022/10/29
 * @since 1.0.0
 */
public class MethodHandleExample 

    private static void withReflectionArgs() 
        Method meth = MethodAccessExampleWithArgs.makeMethod();

        MethodAccessExampleWithArgs mh0 = new MethodAccessExampleWithArgs(0);
        MethodAccessExampleWithArgs mh1 = new MethodAccessExampleWithArgs(1);

        try 
            System.out.println("Invocation using Reflection");
            meth.invoke(mh0, 5, "Jabba the Hutt");
            meth.invoke(mh1, 7, "Boba Fett");
         catch (IllegalAccessException e) 
            e.printStackTrace();
         catch (IllegalArgumentException e) 
            e.printStackTrace();
         catch (InvocationTargetException e) 
            e.printStackTrace();
        
    

    private static void withMethodHandleArgs() 
        MethodHandle mh = MethodAccessExampleWithArgs.makeMethodhandle();

        MethodAccessExampleWithArgs mh0 = new MethodAccessExampleWithArgs(0);
        MethodAccessExampleWithArgs mh1 = new MethodAccessExampleWithArgs(1);

        try 
            System.out.println("Invocation using MethodHandle");
            mh.invokeExact(mh0, 42, "R2D2");
            mh.invokeExact(mh1, 43, "C3PO");
         catch (Throwable e) 
            e.printStackTrace();
        
    

    public static void main(String[] args) 
        withReflectionArgs();
        withMethodHandleArgs();
    

lambda实现说明

参考:Translation of Lambda Expressions

There are a number of ways we might represent a lambda expression in bytecode, such as inner classes, method handles, dynamic proxies, and others. Each of these approaches has pros and cons. In selecting a strategy, there are two competing goals: maximizing flexibility for future optimization by not committing to a specific strategy, vs providing stability in the classfile representation. We can achieve both of these goals by using the invokedynamic feature from JSR 292 to separate the binary representation of lambda creation in the bytecode from the mechanics of evaluating the lambda expression at runtime. Instead of generating bytecode to create the object that implements the lambda expression (such as calling a constructor for an inner class), we describe a recipe for constructing the lambda, and delegate the actual construction to the language runtime. That recipe is encoded in the static and dynamic argument lists of an invokedynamic instruction.

invokedynamic指令

The use of invokedynamic lets us defer the selection of a translation strategy until run time. The runtime implementation is free to select a strategy dynamically to evaluate the lambda expression. The runtime implementation choice is hidden behind a standardized (i.e., part of the platform specification) API for lambda construction, so that the static compiler can emit calls to this API, and JRE implementations can choose their preferred implementation strategy. The invokedynamic mechanics allow this to be done without the performance costs that this late binding approach might otherwise impose.

执行逻辑

  1. 将调用点(CallSite)抽象成Java类
  • 第一次执行invokedynamic指令时,JVM会调用该指令所对应的启动方法(BootStrapMethod)
  • 启动方法会生成调用点,并且绑定至该invokedynamic指令中
  1. 并且将方法调用和方法链接暴露给应用程序。(这些工作原本应该由JVM控制)
  2. 运行过程中,每一条invokedynamic指令将捆绑一个调用点,并且会调用调用点所链接的方法句柄。

例子代码

import java.util.function.Predicate;

/**
 * @author dingqi on 2022/10/29
 * @since 1.0.0
 */
public class LambdaTest 
    public static void main(String[] args) 
        Predicate<Character> isDigit = c -> c >= '0' && c <= '9';
        boolean b = isDigit.test('0');
        System.out.println(b);
        b = isDigit.test('a');
        System.out.println(b);
        String s;
        char c = '1';
        int a = c-'0';
    


查看字节码:

javac LambdaTest.java
javap -verbose LambdaTest.class

以上是关于java lambda之方法句柄&invokedynamic指令的主要内容,如果未能解决你的问题,请参考以下文章

java lambda之方法句柄&invokedynamic指令

Java JVM 动态方法调用指令 invokedynamic 实现分析(以 Lambda 表达式实现原理为例)...

Java语言编程经验之基础语法20-Lambda&方法引用-21-函数式接口&Stream流

Java8新特性——Lambda表达式之基本语法 & 自定义函数式接口

Java8新特性——Lambda表达式之基本语法 & 自定义函数式接口

Java8新特性——Lambda表达式之四大核心函数式接口 & 方法/构造器/数组引用