Java虚拟机以方法作为最基本的执行单元, “栈帧" (Stack Frame) 则是用于支持虚拟机进行方法调用和方法执行背后的数据结构, 它也是虚拟机运行时数据区中的虚拟机栈(Viryual Machine Stack) 的栈元素。
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。
每一个方法从调用开始至执行结束的过程, 都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
栈帧的概念结构图
是一组变量值的存储空间, 用于存放方法参数与方法内部定义的局部变量。在Java程序被编译为Class文件时, 就在方法的Code属性的max_locals 数据项中确定了该方法所需分配的局部变量表的最大容量。
局部变量表的容量以变量槽(Variable Slot)为最小单位。
Java虚拟机规范并没有明确指出一个变量槽应占用的内存大小, 只是很有导向性地说到每个变量槽应该能够存放一个boolean、byte、char、short、int、float、reference 或 returnAddress 类型的数据。
允许变量槽的长度可以随着处理器, 操作系统或虚拟机的实现的不同而发生变化, 保证了即使在64位虚拟机中使用了64位的物理物理内存空间去实现一个变量槽, 虚拟机仍要使用对其和补白的手段让变量槽在外观上看起来与32位虚拟机中的一致。
Java 虚拟机数据类型: boolean、byte、char、short、int、float、reference、returnAddress。
Java虚拟机通过索引定位的方式使用局部变量表, 索引值的范围是从0开始至局部变量最大的变量槽数量。
为了尽可能地节省栈帧耗用的内存空间, 局部变量表中的变量槽是可以重用的, 方法体中定义的变量, 其作用域并不一定会覆盖整个方法体。
一个LIFO的栈, 同局部变量表一样, 操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks 数据项之中。
操作数栈的每一个元素都可以是包括 long 和 double 在内的任意 Java 数据类型。
32位数据类型所占的栈容量为1, 64位数据类型所占的栈容量为2。
Javac 编译器的数据流分析工作保证了在方法执行的任何时候, 操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。
当一个方法刚刚开始执行的时候, 该方法的操作数栈也是空的, 在方法的执行过程中, 会有各种字节码指令往操作数栈中写入和提取内容, 也就是出栈与入栈操作。
操作数栈中元素的数据类型必须与字节码指令的序列严格匹配。
两个不同栈帧作为不同方法的虚拟机栈的元素, 在概念上是完全独立的。但是在大多数虚拟机的实现中都会进行一些优化处理, 另两个栈帧出现一部分重叠, 从而节约一些空间, 并且在方法调用时就可以直接共用一部分数据, 无需进行而外的参数复制传递。
两个栈帧之间的数据共享示意图:
当一个方法开始执行时, 只有两种方式退出这个方法 。
原文:https://www.cnblogs.com/ronnieyuan/p/12184493.html