快捷搜索:

通过字节码分析JDK8中Lambda表达式编译及执行机制

作者: 互联网资讯  发布:2019-10-20

原标题:通过字节码分析JDK8中Lambda表达式编译及执行机制【面试+工作】

方法引用(Method reference)和invokedynamic指令详细分析

invokedynamic是jvm指令集里面最复杂的一条。本文将详细分析invokedynamic指令是如何实现方法引用(Method reference)的。

具体言之,有这样一个方法引用:

interface Encode {    void encode(Derive person);}class Base {    public void encrypt() {        System.out.println("Base::speak");    }}class Derive extends Base {    @Override    public void encrypt() {        System.out.println("Derive::speak");    }}public class MethodReference {    public static void main(String[] args) {        Encode encode = Base::encrypt;        System.out.println;    }}

使用javap -verbose MethodReference.class查看对应字节码:

// 常量池Constant pool:   #1 = Methodref          #6.#22         // java/lang/Object."<init>":()V   #2 = InvokeDynamic      #0:#27         // #0:encode:()LEncode;   #3 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;   #4 = Methodref          #30.#31        // java/io/PrintStream.println:(Ljava/lang/Object;)V   #5 = Class              #32            // MethodReference   #6 = Class              #33            // java/lang/Object   #7 = Utf8               <init>   #8 = Utf8               ()V   #9 = Utf8               Code  #10 = Utf8               LineNumberTable  #11 = Utf8               LocalVariableTable  #12 = Utf8               this  #13 = Utf8               LMethodReference;  #14 = Utf8               main  #15 = Utf8               ([Ljava/lang/String;)V  #16 = Utf8               args  #17 = Utf8               [Ljava/lang/String;  #18 = Utf8               encode  #19 = Utf8               LEncode;  #20 = Utf8               SourceFile  #21 = Utf8               MethodReference.java  #22 = NameAndType        #7:#8          // "<init>":()V  #23 = Utf8               BootstrapMethods  #24 = MethodHandle       #6:#34         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  #25 = MethodType         #35            //  V  #26 = MethodHandle       #5:#36         // invokevirtual Base.encrypt:()V  #27 = NameAndType        #18:#37        // encode:()LEncode;  #28 = Class              #38            // java/lang/System  #29 = NameAndType        #39:#40        // out:Ljava/io/PrintStream;  #30 = Class              #41            // java/io/PrintStream  #31 = NameAndType        #42:#43        // println:(Ljava/lang/Object;)V  #32 = Utf8               MethodReference  #33 = Utf8               java/lang/Object  #34 = Methodref          #44.#45        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  #35 = Utf8               V  #36 = Methodref          #46.#47        // Base.encrypt:()V  #37 = Utf8               ()LEncode;  #38 = Utf8               java/lang/System  #39 = Utf8               out  #40 = Utf8               Ljava/io/PrintStream;  #41 = Utf8               java/io/PrintStream  #42 = Utf8               println  #43 = Utf8               (Ljava/lang/Object;)V  #44 = Class              #48            // java/lang/invoke/LambdaMetafactory  #45 = NameAndType        #49:#53        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  #46 = Class              #54            // Base  #47 = NameAndType        #55:#8         // encrypt:()V  #48 = Utf8               java/lang/invoke/LambdaMetafactory  #49 = Utf8               metafactory// 字节码指令 public static void main(java.lang.String[]);     0: invokedynamic #2,  0              // InvokeDynamic #0:encode:()LEncode;     5: astore_1     6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;     9: aload_1    10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V    13: return// 属性SourceFile: "MethodReference.java"InnerClasses:     public static final #51= #50 of #56; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandlesBootstrapMethods:  0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;    Method arguments:      #25 V      #26 invokevirtual Base.encrypt:()V      #25 V

使用invokedynamic指令生成encode对象,然后存入局部变量槽#1。接着获取getstatic获取java/lang/System类的out字段,最后局部变量槽#1作为参数压栈,invokevirtual虚函数调用System.outprintln方法。

那么invokedynamic到底是怎么生成encode对象的呢?

通过字节码分析JDK8中Lambda表达式编译及执行机制【面试+工作】

1.虚拟机解析

hotspot对invokedynamic指令的解释如下:

      CASE(_invokedynamic): {        u4 index = Bytes::get_native_u4;        ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at;        // We are resolved if the resolved_references field contains a non-null object (CallSite, etc.)        // This kind of CP cache entry does not need to match the flags byte, because        // there is a 1-1 relation between bytecode type and CP entry type.        if (! cache->is_resolved((Bytecodes::Code) opcode)) {          CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode),                  handle_exception);          cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at;        }        Method* method = cache->f1_as_method();        if (VerifyOops) method->verify();        if (cache->has_appendix {          ConstantPool* constants = METHOD->constants();          SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0);          MORE_STACK;        }        istate->set_msg(call_method);        istate->set_callee;        istate->set_callee_entry_point(method->from_interpreted_entry;        istate->set_bcp_advance;        // Invokedynamic has got a call counter, just like an invokestatic -> increment!        BI_PROFILE_UPDATE_CALL();        UPDATE_PC_AND_RETURN; // I'll be back...      }

使用invokedynamic_cp_cache_entry_at获取常量池对象,然后检查是否已经解析过,如果没有就解析反之复用,然后设置方法字节码,留待后面解释执行。那么,重点是这个解析。我们对照着jvm spec来看。

根据jvm文档的描述,invokedynamic的操作数指向常量池一个动态调用点描述符(dynamic call site specifier)。
动态调用点描述符是一个CONSTANT_InvokeDynamic_info结构体:

CONSTANT_InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index;}
  • tag 表示这个结构体的常量,不用管
  • bootstrap_method_attr_index 启动方法数组
  • name_and_type_index 一个名字+类型的描述字段,就像这样Object p放到虚拟机里面表示是Ljava/lang/Object; p

然后启动方法数组结构是这样:

BootstrapMethods_attribute { ... u2 num_bootstrap_methods; {     u2 bootstrap_method_ref;    u2 num_bootstrap_arguments;    u2 bootstrap_arguments[num_boot]    } bootstrap_methods[num_bootstrap_methods];}

就是一个数组,每个元素是{指向MethodHandle的索引,启动方法参数个数,启动方法参数}

MethodlHandle是个非常重要的结构,指导了虚拟机对于这个启动方法的解析,先关注一下这个结构:

CONSTANT_MethodHandle_info { u1 tag;//表示该结构体的常量tag,可以忽略 u1 reference_kind; u2 reference_index;}
  • reference_kind是[1,9]的数,它表示这个method handle的类型,这个字段和字节码的行为有关。
  • reference_index 根据reference_kind会指向常量池的不同类型,具体来说
    • reference_kind==1,3,4 指向CONSTANT_Fieldref_info结构,表示一个类的字段
    • reference_kind==5,8,指向CONSTANT_Methodref_info,表示一个类的方法
    • reference_kind==6,7, 同上,只是兼具接口的方法或者类的方法的可能。
    • reference_kind==9,指向CONSTATN_InterfaceMethodref_info,表示一个接口方法

通过invokedynamic,我们可以得

  1. 名字+描述符的表示(由name_and_type_index给出)
  2. 一个启动方法数组(由bootstrap_method_attr_index给出)

方法调用的字节码指令

2.手动解析

可以手动模拟一下解析,看看最后得到的数据是什么样的。在这个例子中:

  0: invokedynamic #2,  0   //第二个operand总是0

查看常量池#2项:

#2 = InvokeDynamic      #0:#27         // #0:encode:()LEncode;#27 = NameAndType        #18:#37        // encode:()LEncode;BootstrapMethods:  0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;    Method arguments:      #25 V      #26 invokevirtual Base.encrypt:()V      #25 V

得到的名字+描述符是:Encode.encode(),启动方法数组有一个元素,回忆下之前说的,这个元素构成如下:

{指向MethodHandle的索引,启动方法参数个数,启动方法参数}

这里得到的MethodHandle表示的是LambdaMetafactory.metafactory:

#24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;`

启动方法参数有:

  • #25 V
  • #26 invokevirtual Base.encrypt:()V
  • #25 V

在Class文件中,方法调用即是对常量池(ConstantPool)属性表中的一个符号引用,在类加载的解析期或者运行时才能确定直接引用。

3. java.lang.invoke.LambdaMetafactory

先说说LambdaMetafactory有什么用。javadoc给出的解释是:

Facilitates the creation of simple "function objects" that implement one or more interfaces by delegation to a provided MethodHandle, after appropriate type adaptation and partial evaluation of arguments. Typically used as a bootstrap method for invokedynamic call sites, to support the lambda expression and method reference expression features of the Java Programming Language.
When the target of the CallSite returned from this method is invoked, the resulting function objects are instances of a class which implements the interface named by the return type of invokedType, declares a method with the name given by invokedName and the signature given by samMethodType. It may also override additional methods from Object.

LambdaMetafactory方便我们创建简单的"函数对象",这些函数对象通过代理MethodHandle实现了一些接口。
当这个函数返回的CallSite被调用的时候,会产生一个类的实例,该类还实现了一些方法,具体由参数给出

将上面得到的MethodHandle写得更可读就是调用的这个方法:

   public static CallSite LambdaMetafactory.metafactory(MethodHandles.Lookup caller,                                       String invokedName,                                       MethodType invokedType,                                       MethodType samMethodType,                                       MethodHandle implMethod,                                       MethodType instantiatedMethodType);

六个参数,慢慢来。

  1. invokestatic 主要用于调用static关键字标记的静态方法
  2. invokespecial 主要用于调用私有方法,构造器,父类方法。
  3. invokevirtual 虚方法,不确定调用那一个实现类,比如Java中的重写的方法调用。
  4. invokeinterface 接口方法,运行时才能确定实现接口的对象,也就是运行时确定方法的直接引用,而不是解析期间。
  5. invokedynamic 这个操作码的执行方法会关联到一个动态调用点对象(Call Site object),这个call site 对象会指向一个具体的bootstrap 方法(方法的二进制字节流信息在BootstrapMethods属性表中)的执行,invokedynamic指令的调用会有一个独特的调用链,不像其他四个指令会直接调用方法,在实际的运行过程也相对前四个更加复杂。结合后面的例子,应该会比较直观的理解这个指令。

3.1 LambdaMetafactory.metafactory()调用前

要知道参数是什么意思,可以从它的调用者来管中窥豹:

 static CallSite makeSite(MethodHandle bootstrapMethod,                             // Callee information:                             String name, MethodType type,                             // Extra arguments for BSM, if any:                             Object info,                             // Caller information:                             Class<?> callerClass) {        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);        CallSite site;        try {            Object binding;            info = maybeReBox;            if (info == null) {                binding = bootstrapMethod.invoke(caller, name, type);            } else if (!info.getClass().isArray {                binding = bootstrapMethod.invoke(caller, name, type, info);            } else {                Object[] argv =  info;                maybeReBoxElements;                switch (argv.length) {                ...                case 3:                    binding = bootstrapMethod.invoke(caller, name, type,                                                     argv[0], argv[1], argv[2]);                    break;                ...                }            }            //System.out.println("BSM for "+name+type+" => "+binding);            if (binding instanceof CallSite) {                site =  binding;            }  else {                throw new ClassCastException("bootstrap method failed to produce a CallSite");            }            ...        } catch (Throwable ex) {            ...        }        return site;    }

java.lang.invoke.LambdaMetafactory的调用是通过MethodHandle引发的,所以可能还需要补一下MethodHandle的用法,百度一搜一大堆,javadoc也给出了使用示例:

String s;MethodType mt; MethodHandle mh;MethodHandles.Lookup lookup = MethodHandles.lookup();// mt is (char,char)Stringmt = MethodType.methodType(String.class, char.class, char.class);mh = lookup.findVirtual(String.class, "replace", mt);s =  mh.invoke("daddy",'d','n');// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;assertEquals(s, "nanny");

回到源码,关键是这句:

binding = bootstrapMethod.invoke(caller, name, type,                               argv[0], argv[1], argv[2]);

argv[0],argv[1],argv[2]分别表示之前启动方法的三个参数
caller即调用者,这里是MethodReference这个类,然后name和type参见下面的详细解释:

  • MethodHandles.Lookup caller 表示哪个类引发了调动
  • String invokedName 表示生成的类的方法名,对应例子的encode
  • MethodType invokedType 表示CallSite的函数签名,其中参数类型表示捕获变量的类型,返回类型是类要实现的接口的名字,对应例子的()Encode,即要生成一个类,这个类没有捕获自由变量,然后这个类要实现Encode接口(返回类型为生成的类要实现的接口)
    接下来
  • MethodType samMethodType 表示要实现的方法的函数签名和返回值,对于例子的#25 V,即实现方法带有一个形参,返回void
  • MethodHandle implMethod 表示实现的方法里面应该调用的函数,对于例子的#26 invokevirtual Base.encrypt:()V,表示调用Base的虚函数encrypt,返回void
  • MethodType instantiatedMethodType 表示调用方法的运行时描述符,如果不是泛型就和samMethodType一样

关于方法调用的其他详细的解释可以参考官方文档《The Java® Virtual Machine Specification Java8 Edition》-2.11.8 Method Invocation and Return Instructions。

3.2 LambdaMetafactory.metafactory()调用

源码面前,不是了无秘密吗hhh,点进源码看看这个LambdaMetafactory到底做了什么:

     */    public static CallSite metafactory(MethodHandles.Lookup caller,                                       String invokedName,                                       MethodType invokedType,                                       MethodType samMethodType,                                       MethodHandle implMethod,                                       MethodType instantiatedMethodType)            throws LambdaConversionException {        AbstractValidatingLambdaMetafactory mf;        mf = new InnerClassLambdaMetafactory(caller, invokedType,                                             invokedName, samMethodType,                                             implMethod, instantiatedMethodType,                                             false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);        mf.validateMetafactoryArgs();        return mf.buildCallSite();    }

它什么也没做,做事的是InnerClassLambdaMetafactory.buildCallSite()创建的最后CallSite,那就进一步看看InnerClassLambdaMetafactory.buildCallSite()

    @Override    CallSite buildCallSite() throws LambdaConversionException {        // 1. 创建生成的类对象        final Class<?> innerClass = spinInnerClass();        if (invokedType.parameterCount {            // 2. 用反射获取构造函数            final Constructor<?>[] ctrs = AccessController.doPrivileged(                    new PrivilegedAction<Constructor<?>[]>() {                @Override                public Constructor<?>[] run() {                    Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();                    if (ctrs.length == 1) {                        // The lambda implementing inner class constructor is private, set                        // it accessible  before creating the constant sole instance                        ctrs[0].setAccessible;                    }                    return ctrs;                }                    });            if (ctrs.length != 1) {                throw new LambdaConversionException("Expected one lambda constructor for "                        + innerClass.getCanonicalName() + ", got " + ctrs.length);            }            try {                // 3. 创建实例                 Object inst = ctrs[0].newInstance();                // 4. 根据实例和samBase生成MethodHandle                // 5. 生成ConstantCallSite                return new ConstantCallSite(MethodHandles.constant(samBase, inst));            }            catch (ReflectiveOperationException e) {                throw new LambdaConversionException("Exception instantiating lambda object", e);            }        } else {            try {                UNSAFE.ensureClassInitialized(innerClass);                return new ConstantCallSite(                        MethodHandles.Lookup.IMPL_LOOKUP                             .findStatic(innerClass, NAME_FACTORY, invokedType));            }            catch (ReflectiveOperationException e) {                throw new LambdaConversionException("Exception finding constructor", e);            }        }    }

首先它生成一个.class文件,虚拟机默认不会输出,需要下面设置VM option-Djdk.internal.lambda.dumpProxyClasses=.,Dump出虚拟机生成的类我得到的是:

import java.lang.invoke.LambdaForm.Hidden;// $FF: synthetic classfinal class MethodReference$$Lambda$1 implements Encode {    private MethodReference$$Lambda$1() {    }    @Hidden    public void encode(Derive var1) {        var1).encrypt();    }}

该类实现了传来的接口函数(动态类生成,熟悉spring的朋友应该很熟悉)。

回到buildCallSite()源码,它使用MethodHandles.constant(samBase, inst)创建MethdHandle,放到CallSite里面,完成整个LambdaMetafactory的工作。
MethodHandles.constant(samBase, inst)相当于一个总是返回inst的方法。

lambda表达式运行机制

总结

到这里就结束了整个流程,文章有点长,总结一下:

  1. 虚拟机遇到invokedynamic,开始解析操作数
  2. 根据invokedynamic #0:#27获取到启动方法和一个名字+描述符
    其中启动方法是
BootstrapMethods:  0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;    Method arguments:      #25 V      #26 invokevirtual Base.encrypt:()V      #25 V

名字+描述符

 #27 = NameAndType        #18:#37        // encode:()LEncode;
  1. 启动方法指向LambdaMetafactory.metafactory,但是不会直接调用而是通过MethdHandle间接调用。调用位置位于CallSite.makeCallSite()
  2. LambdaMetafactory.metafactory()其实使用InnerClassLambdaMetafactory.buildCallSite()创建了最后的CallSite
  3. buildCallSite()会创建一个.class,
  4. buildCallSite()会向最后的CallSite里面放入一个可调用的MethdHandle
  5. 这个MethodHandle指向的是一个总是返回刚刚创建的.class类的实例的方法,由MethodHandles.constant(samBase, inst)完成
  6. 最后,用invokevirtual调用CallSite里面的MethdHandle,返回.class类的示例,即inst,即new MethodReference$$Lambda$1

在看字节码细节之前,先来了解一下lambda表达式如何脱糖(desugar)。lambda的语法糖在编译后的字节流Class文件中,会通过invokedynamic指令指向一个bootstrap方法(下文中部分会称作“引导方法”),这个方法就是java.lang.invoke.LambdaMetafactory中的一个静态方法。通过debug的方式,就可以看到该方法的执行,此方法源码如下:

百乐门棋牌 1

在运行时期,虚拟机会通过调用这个方法来返回一个CallSite(调用点)对象。简述一下方法的执行过程,首先,初始化一个InnerClassLambdaMetafactory对象,这个对象的buildCallSite方法会将Lambda表达式先转化成一个内部类,这个内部类是MethodHandles.Lookup caller的一个内部类,也即包含此Lambda表达式的类的内部类。这个内部类是通过字节码生成技术(jdk.internal.org.objectweb.asm)生成,再通过UNSAFE类加载到JVM。然后再返回绑定此内部类的CallSite对象,这个过程的源码也可以看一下:

百乐门棋牌 2

百乐门棋牌 3

这个过程将生成一个代表lambda表达式信息的内部类(也就是方法第一行的innerClass,这个类是一个 functional 类型接口的实现类),这个内部类的Class字节流是通过jdk asm 的ClassWriter,MethodVisitor,生成,然后再通过调用Constructor.newInstance方法生成这个内部类的对象,并将这个内部类对象绑定给一个MethodHandle对象,然后这个MethodHandle对象传给CallSite对象(通过CallSite的构造函数赋值)。所以这样就完成了一个将lambda表达式转化成一个内部类对象,然后将内部类通过MethodHandle绑定到一个CallSite对象。CallSite对象就相当于lambda表达式的一个勾子。而invokedynamic指令就链接到这个CallSite对象来实现运行时绑定,也即invokedynamic指令在调用时,会通过这个勾子找到lambda所代表的一个functional接口对象(也即MethodHandle对象)。所以lambda的脱糖也就是在运行期通过bootstrap method的字节码信息,转化成一个MethodHandle的过程。

通过打印consumer对象的className(greeter.getClass().getName())可以得到结果是eight.Functionnal$$Lambda$1/659748578前面字符是Lambda表达式的ClassName,后面的659748578是刚才所述内部类的hashcode值。

下面通过具体的字节码指令详细分析一下lambda的脱糖机制,并且看一下invokedynamic指令是怎么给lambda在JVM中的实现带来可能。如果前面所述过程还有不清晰,还可以参考下Oracle工程师在设计java8 Lambda表达式时候的一些思考:Translation of Lambda Expressions

lambda表达式字节码指令示例分析

先看一个简单的示例,示例使用了java.util.function包下面的Consumer。

示例代码:(下面的Person对象只有一个String类型属性:name,以及一个有参构造方法)

百乐门棋牌 4

用verbose命令看一下方法主体的字节码信息,这里暂时省略常量池信息,后面会在符号引用到常量池信息的地方具体展示。

百乐门棋牌 5

invokedynamic指令特性

可以看到第一条指令就是代表了lambda表达式的实现指令,invokedynamic指令,这个指令是JSR-292开始应用的规范,而鉴于兼容和扩展的考虑(可以参考Oracle工程师对于使用invokedynamic指令的原因),JSR-337通过这个指令来实现了lambda表达式。也就是说,只要有一个lambda表达式,就会对应一个invokedynamic指令。

先看一下第一行字节码指令信息

0: invokedynamic #2, 0

  1. 0: 代表了在方法中这条字节码指令操作码(Opcode)的偏移索引。
  2. invokedynamic就是该条指令的操作码助记符。
  3. #2, 0 是指令的操作数(Operand),这里的#2表示操作数是一个对于Class常量池信息的一个符号引用。逗号后面的0 是invokedynamic指令的默认值参数,到目前的JSR-337规范版本一直而且只能等于0。所以直接看一下常量池中#2的信息。 invokedynamic在常量是有专属的描述结构的(不像其他方法调用指令,关联的是CONSTANT_MethodType_info结构)。 invokedynamic 在常量池中关联一个CONSTANT_InvokeDynamic_info结构,这个结构可以明确invokedynamic指令的一个引导方法(bootstrap method),以及动态的调用方法名和返回信息。

常量池索引位置#2的信息如下:

结合CONSTANT_InvokeDynamic_info的结构信息来看一下这个常量池表项包含的信息。

CONSTANT_InvokeDynamic_info结构如下:

百乐门棋牌 6

简单解释下这个CONSTANT_InvokeDynamic_info的结构:

  • tag: 占用一个字节(u1)的tag,也即InvokeDynamic的一个标记值,其会转化成一个字节的tag值。可以看一下jvm spec中,常量池的tag值转化表(这里tag值对应=18):

百乐门棋牌 7

  • bootstrap_method_attr_index:指向bootstrap_methods的一个有效索引值,其结构在属性表的 bootstrap method 结构中,也描述在Class文件的二进制字节流信息里。下面是对应索引 0 的bootstrap method 属性表的内容:

百乐门棋牌 8

这段字节码信息展示了,引导方法就是LambdaMetafactory.metafactory方法。对照着前面LambdaMetafactory.metafactory的源码一起阅读。通过debug先看一下这个方法在运行时的参数值:

本文由百乐门棋牌发布于互联网资讯,转载请注明出处:通过字节码分析JDK8中Lambda表达式编译及执行机制

关键词:

上一篇:「五个步骤」打造成功EDM邮件文案
下一篇:没有了