01|运行时栈帧结构
Java虚拟机以方法作为最基本的执行单位,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构。栈帧存储类方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。栈帧中需要多大的局部变量表和多深的操作数栈在源码编译期就已经确定,并写入到类方法表的Code属性中,换言之栈帧所需内存的大小不随程序运行期数据的影响,只取决于程序源码和具体虚拟机实现栈内存布局形式。
局部变量表
是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。在java程序被编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量。局部变量表的容量以变量槽为最小单位。
操作数栈
操作数栈是一个后入先出栈,同局部变量表一样,操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中。
动态连接
每个栈帧都包含了一个指向运行时常量池中该栈帧所属方法的应用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数,部分符号引用会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转换被称为静态解析。另外一部分将在每一次运行期间都转换为直接引用,这部分就称为动态连接。
方法返回地址
方法退出方式:
- 正常调用完成:执行引擎遇到任意一个方法会的字节码指令
- 异常调用完成:方法执行时遇到未捕获的异常
02|方法调用
方法调用不等同于方法中的代码被执行,方法调用阶段唯一的任务是确定被调用的具体是哪一个方法,不涉及方法内部的具体运行过程。
解析
所有方法调用的目标方法在Class文件里面都是一个常量池的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转换为直接引用,这种解析能够成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。