Java虚拟机已方法作为最基本的执行单位。
栈帧:是支持Java虚拟机进行方法调用和方法执行背后的数据结构。
栈帧存储了方法的 局部变量表、操作数栈、动态连接和放回地址等信息。
每一个方法的调用开始和执行结束,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
栈帧内存
一个栈桢需要分配多少内存,并不会受程序运行期变量数据的影响,而仅仅取决于程序源码和具体的虚拟机实现的栈内存布局形式。
栈帧运行
对于执行引擎来讲,在活动线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是生效的,其被称为“当前栈帧”,与这个栈帧所关联的方法称为“当前方法”。
1. 局部变量表
局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量
线程安全
由于局部变量表是建立在线程堆栈中的,属于线程私有的数据,无论读写两个连续的变量槽是否为原子操作,都不会引起数据竞争和线程安全问题。
当一个方法被调用时,Java虚拟机会使用局部变量表来完成参数值到参数变量列表的传递过程,即实参到形参的传递。如果执行的是实例方法(没有被static修饰的方法),那局部变量表中第0位索引的变量槽默认是用于传递方法所属对象实例的引用,在方法中可以通过关键字“this”来访问到这个隐含的参数。其余参数则按照参数表顺序排列,占用从1开始的局部变量槽,参数表分配完毕后,再根据方法体内部定义的变量顺序和作用域分配其余的变量槽。
2. 操作数栈
操作数栈也常被称为操作栈,它是一个后入先出栈。
功能:
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。如在做算术运算的时候是通过将运算涉及的操作数栈压入栈顶后调用运算指令来进行的,又如在调用其他方法的时候是通过操作数栈来进行方法参数的传递。举个例子,例如整数加法的字节码指令iadd,这条指令在运行的时候要求操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令时,会把这两个int值出栈并相加,然后将相加的结果重新入栈。
数据共享:
两个不同的栈帧作为不同方法的虚拟机栈元素,是完全相互独立的。但虚拟机的实现里都会进一步优化处理,令两个栈桢重叠,可以节约一部分空间,并且共用一部分数据。
3. 动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所述方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转化被称为静态解析。
另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接。
4. 方法返回地址
当一个方法返回后,只有两种方式退出这个方法。
-
执行引擎遇到任意一个方法返回的字节码指令。
-
方法执行的过程中遇到了异常。
无论采用何种退出方式,在方法退出之后,都必须返回到最初方法被调用的位置
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。笔者这里写的“可能”是由于这是基于概念模型的讨论,只有具体到某一款Java虛 拟机实现,会执行哪些操作才能确定下来。
5. 附加信息
《Java虚拟机规范》允许虚拟机实现增加一些规范里没有描述的信息到栈帧之中,例如与调试、性能收集相关的信息。