Java虚拟机字节码执行引擎(2)

如果在C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限验证,如果通过则返回这个方法的直接引用,查找结束;如果不通过,则返回java.lang.IllegalAccessError异常。

否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。

如果最终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

我们可以看出invokevirtual指令执行的时候是由对象的实际类型决定调用哪个方法版本的,而对象的实际类型只有到运行期的时候才能确定,我们把这种在运行期确定方法版本的分派过程称为“动态分派”。

单分派和多分派

方法的接收者与方法的参数统称为方法的宗量,根据分派基于宗量的多少,将分派分为单分派和多分派两种。

目前的java语言中,静态分派的时候,是基于接收者和方法参数两个宗量进行的,因此静态分派是多分派;动态分派的时候,是基于接收者一个宗量进行的,因此动态分派是单分派。

方法执行

一般从程序代码到物理机可以运行的目标代码都要经历下图所示的过程:

Java虚拟机字节码执行引擎

图中展示了编译执行和解释执行这两种路径,无论是哪种执行方式,一般都会先通过词法分析和语法分析把源代码转化为抽象语法树(AST)。

然后从抽象语法树节点开始,中间的分支代表的是解释执行过程,下边的分支代表的是编译执行过程。

jvm执行字节码是采用的解释执行,javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。而解释器则在虚拟机的内部。

javac编译器输出的字节码指令流是一种基于栈的指令集架构,指令流中的大部分指令都是零地址指令,它们依赖操作数栈进行工作。与之相对的是基于寄存器的指令集。

基于栈的指令集有以下优点:

可移植

代码更加紧凑(不需要存放参数)

编译器实现更简单(不需要考虑空间分配问题,所需空间都在栈上操作)

基于栈的指令集的主要缺点就是执行速度慢:一个是因为基于栈完成相同功能所需要的指令集数量一般比基于寄存器要多,因为入栈、出栈会产生多余的指令数量;另一个是因为栈是基于内存实现的,内存的读取速度本身就比处理器寄存器要慢,这一点可以采取栈顶缓存的手段,把最常用的操作映射到寄存器中避免直接访问内存,但是毕竟只是优化措施,不能解决本质问题。

执行方法中的代码本质就是通过栈的指令集来进行运算,这里就不详述了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f0d378fcd19a79148067c8c021661e9f.html