八、深入理解java虚拟机-虚拟机字节码执行引擎

2019-01-10  本文已影响0人  楊大大大侠

一、概述

执行引擎在执行java代码的时候可能会有解释执行和编译执行;

输入字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。

二、运行时栈帧结构

栈帧:用于支持虚拟机进行方法调用和方法执行的数据结构;它是虚拟机运行时数据区中的虚拟机栈的栈元素;栈帧存储了方法的局部变量、操作数栈、动态链接和方法返回地址等信息。

对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的称为当前栈帧,与这个栈帧相关的方法称为当前方法。

1、局部变量表

用于存放方法参数和方法内部定义的局部变量;容量以变量槽为最小单位,没个slot都应该可以存放一个Boolean、byte、char、short、int、float、reference和returnAddress类型的数据;

虚拟机使用索引定位的方式使用局部变量表;

在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程。如果当前字节码的pc计数器的值已经超过了某个变量的作用域,那这个变量对应的slot就可以交给其他变量使用;

2、操作数栈

先入后出栈。

当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,即出栈/入栈操作。

3、动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。

4、方法返回地址

正常完成出口、异常完成出口。一般来说,方法正常退出时,调用者的pc计数器的值可以作为返回地址,栈帧中可能会保存这个计数器值。而方法异常退出时,返回的地址要通过异常处理表确定,栈帧中一般不会保存这部分信息。

方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数中,调整pc计数器的值以指向方法调用指令后边的一条指令。

5、附加信息

三、方法调用

确定被调用方法的版本。一切方法调用在class文件里边存储的都是符号引用,而不是方法在实际运行时内存布局中的入口。

java虚拟机中提供的5条方法调用字节码指令:

    invokestatic:调用静态方法

    invokespecial:调用实例构造器<init>方法、私有方法和父类方法

    invokeirtual:调用所有的虚方法

    invokeinterface:调用接口方法

    invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法

1、解析调用

在类加载的解析阶段,会将一部分符号引用转换为直接引用。解析成立的前提:编译期可知,运行期不可变。只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段确定唯一的版本;符合这个条件的有静态方法、私有方法、实例构造器、父类方法;

2、分派调用

1>静态分派(重载通过参数的静态类型判断

静态类型:变化仅在使用时发生,变量本身的静态类型不会改变,并且最终的静态类型是在编译期可知的;

实际类型:变化结构在运行期时才可以确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么;

2>动态分派(重写)

invokevirtual指令的多态查找:

    找到操作数栈顶的第一的元素指向的对象的实际类型,记作c(运行期确定接收者的实际类型);

    如果在类型c中找到与产量中的描述符合简单名称都相符的方法,则进行访问权限校验,如果通过,则返回这个方法的直接引用,查找结束;如果不通过,返回异常;

    否则按照继承关系从下往上依次对c的各个父类进行上一步的搜索和验证;如果始终没有找到合适的方法,抛出异常;

3>单分派和多分派

方法的接收者与方法的参数统称为方法的宗量;

静态分派属于多分派;动态分派属于单分派类型;

四、基于栈的字节码解释执行引擎

1、解释执行

2、基于栈的指令集与基于寄存器的指令集

3、基于栈的解释器执行过程

上一篇下一篇

猜你喜欢

热点阅读