虚拟机字节码执行引擎

栈帧

栈帧结构

栈帧是用于支持虚拟机进行方法调用执行背后的数据结构

每一个栈帧都在Java程序进行编译的时候,他需要多大的空间,多么深的操作都被计算好了后写入了Code属性当中,所以一个栈帧需要多少内存只取决于程序源码和虚拟机的栈内存结构

每一个栈帧都包括局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息。

在活动线程线程当中只有位于栈顶的方法才是运行的,只有处于栈顶的栈帧才是生效的,我们称其为“当前栈帧”

局部变量表

局部变量表是一组变量值的存储空间,使用与存放方法参数和方法内部定义的局部变量。在Java程序被编译为Class文件的时候,就在方法的Code属性当中的max_locals数据项当中确定了该方法所需分配的局部变量表的最大容量。

局部变量表的容量是以变量槽为最小单位,《Java虚拟机规范》中并没有明确规定出变量槽应占内存空间的大小,而是说每个变量槽都应能放下一个short,byte,bollean,char,int,char,float,reference或returnAddress类型数据的容量。

它允许变量槽的长度可以随着处理器,操作系统或虚拟机实现的不同而发生变化,保证了即使在一个64位的虚拟机当中使用了64位的物理内存去实现一个变量槽,虚拟机任要使用对齐和对白的手段让变量槽看起来和32位虚拟机一致。

操作数栈

操作数栈是一个先入后出的栈,同局部变量表一样,操作数栈的最大深度也是在编译的时候被写入Code属性的max_stacks项中。

32位数据类型所占栈的容量为1,64位数据类型所占栈的容量为2,Javac编译器的数据流分析工作保证了在方法执行的任何时候,操作数栈的深度都不会超过max_stacks的最大值。

操作数栈中的元素的数据类型必须与字节码指令的序列严格匹配,在类校验阶段也要验证这一点。以iadd指令为例,他指两个整数相加,所以在栈顶和靠近栈顶的两个数必须是int值。

动态连接

每个栈帧都包括一个执行运行时常量池中该栈帧的方法引用,持有这个引用是为来支持方法调用过程当中的动态连接。也就是将符号引用转化为直接引用。

方法返回地址

一个方法开始运行之后,只有两种方式会退出这个方法:1.正常调用完成2.异常调用完成

  1. 正常调用完成:执行引擎遇到任意一个方法返回的字节码指令,这个时候可能会将返回值传递给上层的方法调用者。
  2. 异常调用完成:在方法执行的时候遇到了异常,并且这个异常没有在方法体内得到妥善处理。无论是Java虚拟机出现了异常还是athrow字节码指令产生的异常,只要没有在本方法的异常处理表当中找到匹配的异常处理器,就会导致方法退出。

无论什么方式退出,都会回到最初方法被调用的位置 ,也就是主调方法PC计数器所记录的位置。

一般来说,方法正常退出的时候,主调方法PC计数器的值就可以作为返回地址,然后这个方法的栈顶就会保存这个信息。

异常退出不会保存。

分派

静态分派

在编译期间根据方法调用者和方法的参数类型来调用方法,静态方法不依赖于对象的实际类型,而是基于引用类型的声明类型。

也就是说下面这段代码,他会根据Human类型来执行不会根据Man类型执行。

Human man =new Man();

“man继承于Human是他的子类”

动态分派

运行的时候根据对象的实力类型来确定调用哪个方法,是实现多态的关键机制。

在虚拟机当中重载是以参数的静态分派作为依据而不是实际类型的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下水道程序员

你的鼓励将是我奋斗的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值