JVM学习笔记六:字节码执行引擎

运行时栈帧结构

栈帧是虚拟机运行时数据区中的虚拟机栈的栈元素,栈帧存储了方法的局部变量表,操做数栈,动态链接,方法返回地址,在编译程序代码的时候,栈帧中须要多大的局部变量表、多深的操做数栈都已经彻底肯定了,而且写入到方法表的Code属性之中。
jinjiprojectnaotujava

局部变量表

其最大容量由方法的Code属性,max_locals肯定,局部变量表的容量以变量槽(Slot)为最小单位,Long和Double占用两个空间,其读写是非原子性的,其会占用n与n+1两个Slot,不容许采用任何方式单独访问其中的一个Slot。性能

在方法执行时,虚拟机是使用局部变量表来完成参数值到变量表的传递,若是执行的是实例方法,那么局部变量表第0位索引的Slot默认是用于传递方法所属的对象实例的引用,其他参数从索引1开始占用Slot,参数表分配完毕后,再根据方法体内部定义的变量顺序和做用域分配其他的Slot,为了尽量的节省栈帧空间,局部变量表中的Solt是能够重用的,当PC计数器的值,已经超出方法体中定义的变量的做用域时,其占用的Slot会被其余做用域的变量重用。对象

操做数栈

其最大深度由方法的Code属性的max_stacks决定,有些虚拟机实现上,会让相邻的两个栈帧出现一些 共享部分,方便方法调用之间共享数据。blog

动态链接

每一个栈帧都包含一个指向运行时常量池中该帧所属方法的引用,这个引用是为了支持方法调用过程当中的动态链接。继承

方法返回地址

当一个方法执行之后,只有两种方式能够退出这个方法,一个是执行引擎遇到一个返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,这种退出方法被称为正常完成出口,另外一种退出方式是,在方法的执行过程当中遇到了异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会致使方法退出,这种退出方式被称为异常完成出口,这种方式是不会给它的上层调用者返回任何返回值的。索引

不管任何方式退出,都须要返回到方法被调用的位置,程序才能继续执行,因此方法退出时会恢复上层方法的局部变量表和操做数栈,把返回值压入调用者的操做数栈,调整PC至下一条指令地址。ip

方法调用

方法调用阶段惟一的任务就是肯定被调用方法的版本作用域

方法调用的方式

方法的调用包括解析与分派两种方式虚拟机

  1. 解析能够在类加载的解析阶段,将符号引用直接转化为直接引用,这种解析成立的前提是,方法的运行以前就能够肯定调用的版本是惟一的,并且是在运行期不可变的。
  2. 分派又分为静态分派与动态分派,重载机制就是由静态分派实现,而重写则是由动态分派实现。

动态分派的解析过程(invokevirtual指令为例):编译

  1. 找到操做数栈顶的第一个元素所指向的对象的实际类型,记作C。
  2. 若是在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,若是经过则返回这个方法的直接引用,查找过程结束,不然,返回java.lang.IllegalAccessError
  3. 不然,按照继承关系从下往上对C的各个父类进行第2步的搜索和验证过程。
  4. 若是没有找到合适的方法,抛出java.lang.AbstractMethodError异常。

出于性能的考虑,会为类在方法区创建一个虚方法表,使用虚方法表索引来代替元数据查找以提升性能
jinjiprojectnaotu

参考资料

本文参考:《深刻理解Java虚拟机》

相关文章
相关标签/搜索