JVM-invokedynamic指令分析

       invokedynamic指令是java7引入的。这是自 1.0 以来第一次引入新的虚拟机指令。到了 java 8 这条指令才第一次在 java 应用,用在 lambda 表达式里。在这之前的方法调用指令为 invokestatic、invokespecial、invokevirtual、invokeinterface。这四种指令和包含目标方法类名,方法名以及方法描述符的符号引用绑定。为了实现invokedynamic,java7引入了更底层,更灵活的方法抽象:方法句柄。

方法句柄的概念
        方法句柄是一个强类型,可以直接执行的引用。方法句柄的类型是由所指向方法的参数类型和返回类型组成。它是用来确认方法句柄是否适配的唯一关键。方法句柄的功能与反射想类似。下面从不同的角度对比一下反射-代理-方法句柄

        反射需要调用setAccesible(),可能会受到禁止警告,代理类如果是内部类创建,就只能访问受限的函数和方法,而方法句柄在访问的上下文中对所有方法都拥有完整的权限。
        反射的执行速度回受可变长的参数,基本类型的自动装拆箱,以及最重要的方法内联的影响,代理调用的方法和调用函数的方式是一样的,速度最快,方法句柄自然不可能有代理的执行速度,它的速度受jvm的各类配置所影响。
方法句柄的调用
        方法句柄的调用有两种,一种是需要严格匹配参数类型的invokExact:假设一个方法句柄需要接受一个Object类型的参数,如果你传入一个String类型的参数,那么在运行时就会抛出异常。但方法句柄API有个注解@PolymorphicSignature,如果被调用的方法有这个注解的话,编译器会根据所所传入的参数来生成方法描述符,而不是采用目标方法的方法描述符。

        另一种是invoke,可以自动适配参数类型。invoke方法会调用MethodHandle.asType方法,生成一个适配器句柄。在调用目标方法时先进行适配,然后再返回给调用者。

方法句柄的操作
        jvm在调用invokExact方法的时候,会调用一个共享的、与方法句柄类型相关的特殊适配器LambdaForm当中。当然,要想看这个类的话,可以添加启动参数  -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FIFLE=true 来导出这个类的class文件。

       在LambdaForm中会调用Invokers.checkExactType去检查参数类型,然后调用Invokes.CheckCustomized方法。最后,调用方法句柄的invokeBasic方法。invokeBasic同样会被jvm特殊对待,执行时,会调用到方法句柄本身所持有的适配器里,也是一个LambdaForm。这个LambdaForm会获取方法句柄里的MemberName字段,作为参数调用linkToStatic方法。当然,linkToStatic也会做特殊处理,会根据传入的参数 储存的索引  直接跳转到目标方法。  在这些方法里 Invokes.CheckCustomized在超过 一个阈值时就会进行优化  阈值可用参数 -Djava.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD进行设置,默认是127.当调用次数大于这个阈值的时候,Invokes.CheckCustomized会为该方法句柄生成一个特有的适配器,这个适配器就会将方法句柄作为常量 直接获取MemberName类型的字段 供linkToStatic调用。

        方法句柄的调用和反射一样,都是间接调用,因此  ,会被无法进行方法内联而影响性能。不过,方法句柄的内联瓶颈在于JIT能否将方法句柄作为常量。下面,就来具体的讲讲

invokedynamic指令
        invokedynamic是java7引入的新指令,主要是用来支持动态语言的方法调用。将调用点 抽象成一个java类,并将原本由jvm控制的方法调用以及方法链接暴露给了应用程序。在运行过程中,每条invokedynamic指令都会绑定一个调用点,且会调用该调用点链接的方法句柄。在第一次执行invokedynamic时,jvm会调用该指令对应的启动方法,来生成调用点,并绑定到invokedynamic指令里。之后再次运行的话,就直接调用绑定的调用点链接的方法句柄。
 

你可能感兴趣的:(java)