深入理解JVM(栈帧与方法调用篇)
前言
运行时栈结构
Java虚拟机以方法作为最基本的执行单元,“栈帧”则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,也是虚拟机中运行时数据区中的虚拟机栈中的栈元素。 每一个栈帧都包括了局部变量表、操作数栈、动态链接和方法返回地址以及一些额外的附加信息。
局部变量表
局部变量表是一组变量值得储存空间,用于存放方法参数和方法内部定义的局部变量。
操作数栈
是一个后入先出的栈,用于存放各种字节码指令往栈中写入和提取内容,在大多数虚拟机中会对操作数栈和局部变量表进行优化,把两者进行一部分的重叠,可以使得进行方法调用的时候可以直接操作,不需要传递参数。
动态连接
每个栈帧中都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。 前面讲到过在常量池中的符号引用一部分会在解析的时候转变为直接引用,这一部分是在编译的时候已经能够确定可调用的版本。但是另外一些不能确定的需要在运行的时期进行,这些被称为动态连接。
方法返回地址
一个方法开始执行之后,只有两种退出方法,一种是执行引擎遇到一个返回码,另外一个则是方法遇到异常退出。无论哪种方法都会在方法退出之后返回到最初方法被调用的地方。
附加信息
《Java虚拟机规范》允许虚拟机实现增加一些规范中没有的信息到栈帧中,这些信息被称为附加信息。
方法调用
方法调用指的并非方法中的代码被执行,而是确定需要调用方法的版本。为了确定方法的版本,Java中存在解析和分派两种方法。
解析
如果方法在程序真正运行前就能确定一个版本,并且这个方法的调用版本在运行期是不可变的,那么虚拟机会通过解析的方式来进行方法调用。
分派
静态分派
所有依赖静态类型来决定方法执行版本的分派动作,都称之为静态分派。 静态分派最典型的就是方法重载。
动态分派
动态分派最典型的是方法重写(Override)。
单分派与多分派
方法的接收者与方法的参数统称为方法的宗量。根据宗量的不同可以将分派划分为多分派和单分派。Java语言是一门静态多分派,动态单分派的语言。
