虚拟机方法执行

栈帧

  • 虚拟机栈中存储的是一个个栈帧,每个栈帧代表一个方法体,一个栈帧的进栈与出栈对应着一个方法的调用开始和结束。

  • 每一个栈帧都包含局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息,局部变量表和操作数栈所需要多大空间已经在编译器确定下来;

  • 执行引擎只会处理位于栈顶的栈帧,这个栈帧称为当前栈帧。

局部变量表

  • 存放方法参数和方法内的局部变量
  • 最小单位为Slot,除了long和double数据类型变量占用2个Slot,其他数据类型占用1个Slot
  • 当前方法为实例方法时(非static方法),局部变量表的第0位Slot存储方法所属对象实例的应用“this”
  • 变量表顺序——“this”->参数表->方法体内局部变量

操作数栈

  • 32位数据类型占用1个栈容量,64位占用2个
  • 优化:让下面栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠,方法调用时共用一些数据,减少额外的参数复制传递

动态连接

  • 指向运行时常量池中该栈帧所属方法的引用

方法返回地址

返回到方法被调用的位置,恢复上层方法的局部变量表和操作数栈,将返回值(有的话)压如调用者栈帧的操作数栈中,调整PC计数器指向方法调用指令后面的一条指令。

  • 正常退出
  • 异常退出,匹配方法的异常表

方法调用

方法调用阶段只是确定被调用方法的版本,就是要调用哪一个方法。

  • 解析
    • “编译期可知,运行期不可变”的方法,只会有一个版本的方法:静态方法、私有方法、父类方法、实例构造器方法、final方法。这几个方法在类加载阶段进行了静态分析,将符号引用转化为了直接引用。
    • 其他方法称为“虚方法”,将会根据运行期的不同进行不懂解析,称为动态绑定。
  • 分派–动态绑定
    • Java多态性的体现
    • 静态分派(重载)
      • 编译器对静态类型可知,对实际类型不可知,运行时才对实际类型可知
      • Human man = new Man();Human是静态类型,Man是实际类型,而sr.sayHello(man)中,编译器可知man的静态类型为Human,而不知实际类型Man;
      • 虚拟机(其实是编译器)在重载时通过参数的静态类型而不是实际类型作为判断依据的
    • 动态分派(重写)
      • invokevirtual指令在运行时的解析动作
        • 1)找到操作数栈顶的第一个元素所指向的对象的实际类型,计做C;
        • 2)检查C的合法性
      • 虚拟机在重写是通过实际类型决定方法的版本
      • Human man = new Man(); man.sayHello();
      • 执行的是实际类型Man中的方法。
基于栈的指令集与基于寄存器的指令集
  • 基于栈的指令集,
    • 优点:可移植(一次编写,到处运行),代码紧凑
    • 缺点:执行速度慢,指令数量多,内存访问多
    • 完成相同的功能,栈架构所需要的指令数一般多于寄存器架构,栈实现在内存中,频繁的栈访问意味着频繁的内存访问,这将降低代码的执行速度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值