invokedynamic指令
invokedynamic指令与MethodHandle机制的作用是一样的,都是为了解决原有4条“invoke*”指令方法分派规则完全固化在虚拟机之中的问题,把如何查找目标方法的决定权从虚拟机转嫁到具体用户代码之中,让用户(广义的用户,包含其他程序语言的设计者)有更高的自由度。而且,它们两者的思路也是可类比的,都是为了达到同一个目的,只是一个用上层代码和API来实现,另一个用字节码和Class中其他属性、常量来完成。
每一处含有invokedynamic指令的位置都被称作“动态调用点(Dynamically-Computed Call Site)”,这条指令的第一个参数不再是代表方法符号引用的CONSTANT_Methodref_info常量,而是变为JDK7时新加入的CONSTANT_InvokeDynamic_info常量,从这个新常量中可以得到3项信息:引导方法(Bootstrap Method,该方法存放在新增的BootstrapMethods属性中)、方法类型(MethodType)和名称。引导方法是有固定的参数,并且返回值规定是java.lang.invoke.CallSite对象,这个对象代表了真正要执行的目标方法调用。根据CONSTANT_InvokeDynamic_info常量中提供的信息,虚拟机可以找到并且执行引导方法,从而获得一个CallSite对象,最终调用到要执行的目标方法上。
InvokeDynamic指令演示:
package com.test;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class InvokeDynamicTest {
public static void main(String[] args) throws Throwable {
INDY_BootstrapMethod().invokeExact("icyfenix");
}
public static void testMethod(String s){
System.out.println("hello String:" + s );
}
public static CallSite BootstrapMethod(MethodHandles.Lookup lookup,
String name,MethodType mt) throws Throwable{
return new ConstantCallSite(
lookup.findStatic(InvokeDynamicTest.class, name, mt));
}
private static MethodType MT_BootstrapMethod(){
return MethodType
.fromMethodDescriptorString(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;"
+ "Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", null);
}
private static MethodHandle MH_BootstrapMethod() throws Throwable {
return MethodHandles.lookup().findStatic(InvokeDynamicTest.class,
"BootstrapMethod",
MT_BootstrapMethod());
}
private static MethodHandle INDY_BootstrapMethod() throws Throwable {
CallSite cs = (CallSite)MH_BootstrapMethod().invokeWithArguments(
MethodHandles.lookup(),
"testMethod",
MethodType.fromMethodDescriptorString("(Ljava/lang/String;)V", null));
return cs.dynamicInvoker();
}
}
由于invokedynamic指令面向的主要服务对象并非Java语言,而是其他Java虚拟机之上的其他动态类型语言,因此,光靠Java语言的编译器Javac的话,在JDK7时甚至还完全没有办法生成带有invokedynamic指令的字节码(曾经有一个java.dyn.InvokeDynamic的语法糖可以实现,后来别取笑了),而到了JDK8引入了Lambda表达式和接口默认方法后,Java语言才算享受到了一点invokedynamic指令的好处,但用Lambda来解释invokedynamic指令运作就比较别扭,也无法与前面的MethodHandle的例子对应类比,所以采用变通的办法:John Rose(JSR 292的负责人)编写过一个把程序字节码转换为使用invokedynamic的简单工具INDY来完成这件事,使用这个工具来产生最终需要的字节码,因此上述代码中的方法名称不能随意改动,更不能把几个方法合并到一起写,因为它们是要被INDY工具读取的。
《深入理解Java虚拟机》第三版学习
网友评论