1. 栈与栈帧整体结构
线程调用一个方法执行和退出就对应着一个栈帧的入栈和出栈,栈的顶部第一个栈帧叫当前栈帧,对应着一个线程最新执行的方法;栈帧内部主要包括局部变量表,操作数栈,动态链接,方法返回地址等信息,下面我们就来逐一介绍
2. 局部变量表
局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。
主要包括编译器就可知道的各种基本数据类型,对象引用等。所以局部变量所需要的容量大小编译器就能确定下来
3.操作数栈
主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。当一个方法执行过程中,会有各种字节码指令对操作数出栈和入栈操作
例如在做算数运算的时候是通过将运算涉及的操作数压入栈顶后调用运算指令来进行的,比如字节码指令iadd,这条指令在运行的时候要 求操作数栈中最接近栈顶的两个元素相加之和;先将栈顶两个元素出栈,相加之后的结果入栈
4.动态连接
每个栈帧都包含一个指向运行时常量池中该"栈帧所属方法"的引用,持有这个引用是为了支持方法调用过程中的动态连接。在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在Class文件的常量池中。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态连接的作用就是为了将这些符号引用转换为调用方法的直接引用
简单来说:被调用的目标方法在编译器无法被确定下来,只能够在程序运行期将方法的符号引用转为直接引用,这种引用转换过程具备动态性,称为动态连接
直接引用:你拥有你所需要数据的地址值,可以直接根据地址值获取到数据。
符号引用:符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。这样,对于其他类的符号引用必须给出类的全名。对于其他类的字段,必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。这样我们就能根据符号引用锁定唯一的类,方法或字段了。
5.方法返回地址
方法正常退出:一个方法正常执行完成之后,会遇到返回指令,这种情况会有一个预先定义好类型的返回值给调用方法
方法异常退出:一个方法执行过程中,出现了异常,并且异常中没有再方法中妥善的捕获处理,那也会触发方法的退出,这种情况不会有返回值返回给调用方的
方法正常退出时,主调方法的程序计数器的值就可以作为返回地址,栈帧中很有可能回保存这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中就一般不会保存这部分信息
本质上,方法的退出就是栈帧出栈的过程,此时,需要恢复上层方法的局部变量表,操作数栈,将返回值压入调用者栈帧的操作数栈、设置计数器值等,让调用者方法继续执行下去