• 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虚拟机》

  • 相关阅读:
    ifcfgens33 xsync async
    MySQL 忘记 root密码 两种 处理方法
    MySQL 用户 权限管理 按需分配 mysql 权限
    ip
    Mac设置su root密码
    odbc 通过 sql server 客户端 连接 mysql
    MySQL 忘记 root密码 处理方法
    mysql 查询缓存 相关变量 参数
    windows 如何 远程桌面 连接 mac
    图像清晰度的评价指标
  • 原文地址:https://www.cnblogs.com/MinnieChang/p/7337413.html
Copyright © 2020-2023  润新知