措施引用(Method reference)和invokedynamic指令详细分析

js金沙6629 1

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

此刻运转时栈帧结构如下:

方法援引(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对象的呢?

js金沙6629 2

3. java.lang.invoke.LambdaMetafactory

先说说拉姆daMetafactory有何用。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.

拉姆daMetafactory方便大家创立轻松的”函数对象”,那几个函数对象通过代理MethodHandle达成了风度翩翩部分接口。
当以此函数重回的CallSite被调用的时候,会发出八个类的实例,该类还落到实处了意气风发部分措施,具体由参数给出

将上面拿到的MethodHandle写得更可读正是调用的那么些艺术:

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

三个参数,慢慢来。

因此打字与印刷consumer对象的className(greeter.getClass().getName()卡塔尔国能够赢得结果是eight.Functionnal$$拉姆da$1/659748578前面字符是拉姆da说明式的ClassName,后边的659748578是刚才所述里面类的hashcode值。

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_js金沙6629,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给出)

js金沙6629 3

3.2 LambdaMetafactory.metafactory()调用

源码前边,不是了无秘密吗hhh,点进源码看看这些拉姆daMetafactory到底做了怎么:

     */    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里面,实现整个拉姆daMetafactory的干活。
MethodHandles.constant(samBase, inst)也正是三个三回九转回到inst的方法。

js金沙6629 4

总结

到那边就得了了百分之百流程,作品有一些长,总括一下:

  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

js金沙6629 5

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一样

措施调用的字节码指令

js金沙6629 6

Lambda不仅仅用起来很方便,品质表以往好些个意况也比无名内部类好,品质方面能够参照他事他说加以考查一下Oracle的SergeyKuksenko揭橥的 Lambda 品质报告。由上文可以看到,即使在运转时索要转接Lambda
Form(见MethodHandle的form属性生成进程),何况生成CallSite,可是随着调用点被反复调用,通过JIT编写翻译优化等,质量会有肯定提高。並且,运转时脱糖也提升了编写翻译期的灵活性(其实在看字节码在此以前,一向感觉Lambda或者是在编写翻译期脱糖成多少个无名内部类的Class,实际不是经过提供叁个boortrap方法,在运作时链接到调用点卡塔 尔(英语:State of Qatar)。运转时生成调用点的不二秘诀实在的内部存款和储蓄器使用率在大部状态也是自惭形秽佚名内部类(java8
从前版本的写法)的法子。所以,在能应用lambda表达式的地点,大家尽量结合实际的质量测量试验景况,写简洁的表达式,尽量收缩Lambda表明式内部捕获变量(因为如此会创设额外的变量对象卡塔 尔(英语:State of Qatar),假诺急需在表明式内部捕获变量,能够假造是还是不是能够将变量写成类的积极分子变量,也即尽量少给Lambda传多余的参数。希望本文能给拉姆da的使用者一些参阅。回到今日头条,查看越来越多

结语

从字节码看lambda能够追溯到根源,所以也就能够了解运维时的内部存款和储蓄器模型。

js金沙6629 7

js金沙6629 8

在看字节码细节以前,先来打听一下lambda表明式怎么着脱糖(desugar卡塔尔。lambda的语法糖在编译后的字节流Class文件中,会透过invokedynamic指令指向一个bootstrap方法(下文中部分会称作“教导格局”卡塔尔国,那几个主意正是java.lang.invoke.拉姆daMetafactory中的三个静态方法。通过debug的方法,就足以看看该办法的施行,此措施源码如下:

上述能够看看Consumer接口的泛型被擦除(编写翻译时期开展,所以字节码新闻中并不会含有泛型消息卡塔尔国,所以这里并不知道实际的参数操作数类型。可是此间能够拿到实在指标的引用值,这里accept方法实施,greeter和person援引出栈,如下图:

CONSTANT_InvokeDynamic_info结构如下:

  1. 第二条指令:5: astore_1
    指令初阶偏移地点是5,主要决定于前面一个下令(invokedynamic卡塔尔国有八个操作数,每一个操作数占两个字节(u2)空间,所以第二条指令正是从字节偏移地点5起头(后续的撼动地址将不再解释卡塔 尔(英语:State of Qatar)。此命令执行后,当前形式的栈帧结构如下(注:此图未有画出如今栈帧的动态链接以致重返地址的数据结构,图中:右侧局地变量表,右边操作数栈卡塔 尔(阿拉伯语:قطر‎:

在运维时代,虚构机缘通过调用那一个措施来回到三个CallSite(调用点卡塔 尔(英语:State of Qatar)对象。简述一下方法的实践进度,首先,发轫化一个InnerClassLambdaMetafactory对象,那一个指标的buildCallSite方法会将Lambda表明式先转形成叁个里边类,那一个里面类是MethodHandles.Lookup
caller的一个里面类,也即含有此拉姆da表明式的类的里边类。那个里面类是因而字节码生成技术(jdk.internal.org.objectweb.asm)生成,再通过UNSAFE类加载到JVM。然后再回来绑定此个中类的CallSite对象,那几个进度的源码也足以看一下:

js金沙6629 9

简言之表明下这么些CONSTANT_InvokeDynamic_info的结构:

常量池索引地点#2的音信如下:

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

综上,已经介绍了lombda表明式在字节码上的达成情势。别的指令,若是对字节码指令感兴趣能够世袭阅读,已经精通的能够略过,本小节和lambda本人并未有太大关系。

此处为了画图方便,所以根据局地变量表和操作数栈的实际上分配空间先画出了多少个格子。因为字节码音信中曾经告知了[stack=4, locals=2, args_size=1]。约等于局部变量表的骨子里运维时间和空间中最大占用八个Slot(一个Slot叁个字节,long,double类型变量需占用五个slot卡塔 尔(英语:State of Qatar),操作数栈是4个slot,参数占三个slot。这里的args是main方法的String[]
args参数。因为是个static方法,所以也未曾this变量的aload_0 指令。

  • tag:
    占用一个字节(u1)的tag,也即InvokeDynamic的几个标识值,其会转形成一个字节的tag值。能够看一下jvm
    spec中,常量池的tag值转变表(这里tag值对应=18卡塔 尔(阿拉伯语:قطر‎:

上面通超过实际际的字节码指令详细解析一下lambda的脱糖机制,并且看一下invokedynamic指令是怎么给lambda在JVM中的达成带给大概。假若眼前所述进程还恐怕有不显著,还是能够参照下Oracle程序猿在筹划java8
拉姆da表达式时候的风度翩翩部分思维:Translation of 拉姆da Expressions

先看一下率先行字节码指令音信

js金沙6629 10

js金沙6629 11

这段字节码信息显示了,教导方式正是LambdaMetafactory.metafactory方法。对照着眼前兰姆daMetafactory.metafactory的源码一起读书。通过debug先看一下那几个方法在运转时的参数值:

js金沙6629 12

invokedynamic指令性子

js金沙6629 13


name_and_type_index:代表常量池表消息的一个有效索引值,其针对性的常量池属性表结构自然是二个CONSTANT_NameAndType_info属性,代表了艺术名称和措施描述符消息。再顺着
#44索引看一下常量池相关项的汇报内容:

  1. 0: 代表了在点子中那条字节码指令操作码(Opcode卡塔尔的偏移索引。
  2. invokedynamic正是该条指令的操作码助记符。
  3. #2, 0
    是命令的操作数(Operand卡塔 尔(英语:State of Qatar),这里的#2意味操作数是贰个对于Class常量池音信的一个符号引用。逗号后边的0
    是invokedynamic指令的暗中同意值参数,到当前的JS帕杰罗-337标准版本一向而且一定要等于0。所以一贯看一下常量池中#2的音讯。
    invokedynamic在常量是有专项的陈诉结构的(不像任何方法调用指令,关联的是CONSTANT_MethodType_info结构卡塔 尔(英语:State of Qatar)。
    invokedynamic
    在常量池中关系二个CONSTANT_InvokeDynamic_info结构,那个结构能够鲜明invokedynamic指令的叁个辅导情势(bootstrap
    method),以致动态的调用方法名和重返消息。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website