原始博客的地址:
https://lyldalek.notion.site/JVM-0x7-fa280975b6f947e3bb8077d3e1685b5b
该项目的地址:
上一篇我们实现了方法调用以及参数传递,主要讨论了四个invoke指令。但是有一条指令我们没有讨论,那就是在JDK 7里面新增的invokedynamic
指令。
这条新增加指令的目标:实现动态类型语言(Dynamically Typed Language)支持而进行的改进之一,也是为JDK 8里可以顺利实现Lambda表达式而做的技术储备。
我们先简单的讨论一下动态类型语言是什么?看一个例子:
obj.println("hello world");
现在先假设这行代码是在Java语言中,并且变量obj的静态类型为java.io.PrintStream,那变量obj的实际类型就必须是PrintStream的子类(实现了PrintStream接口的类)才是合法的。否则,哪怕obj属于一个确实包含有println(String)方法相同签名方法的类型,但只要它与PrintStream接口没有继承关系,代码依然不可能运行——因为类型检查不合法。
但是相同的代码在ECMAScript(JavaScript)中情况则不一样,无论obj具体是何种类型,无论其继承关系如何,只要这种类型的方法定义中确实包含有println(String)方法,能够找到相同签名的方法,调用便可成功。
产生这种差别产生的根本原因是Java语言在编译期间却已将println(String)方法完整的符号引用(本例中为一项CONSTANT_InterfaceMethodref_info常量)生成出来,并作为方法调用指令的参数存储到Class文件中,例如下面这个样子:
invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
这个符号引用包含了该方法定义在哪个具体类型之中、方法的名字以及参数顺序、参数类型和方法返回值等信息,通过这个符号引用,Java虚拟机就可以翻译出该方法的直接引用。
而ECMAScript等动态类型语言与Java有一个核心的差异就是变量obj本身并没有类型,变量obj的值才具有类型,所以编译器在编译时最多只能确定方法名称、参数、返回值这些信息,而不会去确定方法所在的具体类型。
JDK 7新加入了一个java.lang.invoke包,这个包的主要目的是在之前单纯依靠符号引用来确定调用的目标方法这条路之外,提供一种新的动态确定目标方法的机制,称为“方法句柄”(Method Handle)。
举个例子,如果我们要实现一个排序函数,在C/C++中的常用做法是用函数指针,像这样:
void sort(int list[], const int size, int (*compare)(int, int))