zoukankan      html  css  js  c++  java
  • JVM学习笔记六:字节码执行引擎

    运行时栈帧结构

    栈帧是虚拟机运行时数据区中的虚拟机栈的栈元素,栈帧存储了方法的局部变量表,操作数栈,动态连接,方法返回地址,在编译程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中。
    jinjiprojectnaotu

    局部变量表

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

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

    操作数栈

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

    动态连接

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

    方法返回地址

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

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

    方法调用

    方法调用阶段唯一的任务就是确定被调用方法的版本

    方法调用的方式

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

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

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

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

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

    参考资料

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

  • 相关阅读:
    java的访问控制修饰符
    js将一个数组分割成二维子数组
    java的数组
    Java 变量类型
    java
    java的 %和 ^
    Python第三方库 -> 由于是ipynb格式,所以没有对应的输出结果
    获取浏览器历史记录 browserhistory
    虚拟机 CentOS 6.8 安装
    解决 django.db.utils.OperationalError: (1051, "Unknown table 'mydorm.users_studen t'")
  • 原文地址:https://www.cnblogs.com/MinnieChang/p/7337413.html
Copyright © 2011-2022 走看看