JVM栈帧内部结构-方法返回地址

方法返回地址(Return Address):

存放调用该方法的PC寄存器的值。

方法结束方式:

1) 正常结束
2) 出现未处理异常,非正常退出(通过异常完成出口退出的不会给他的上层调用者生产任何的返回值

无论通过哪种方式退出,在方法退出后到该方法被调用的位置,方法正常退出时,调用者的PC计数器的值作为返回地址,即调用该方法的指令的下一条指令地址,而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。

Return:

        当一个方法开始执行后,执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,称正常完成出口

        字节码指令中,返回指令包含ireturn(当返回值是boolean、byte、char、short、int类型时使用)lreturn、freturn、dreturn、areturn(return指令供声明void的方法、实例初始化方法、类和接口的初始化方法使用)。

JVM栈帧内部结构-动态链接

动态链接(或运行时常量池的方法引用):

        每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令

        在Java源文件被编译到字节码文件时,所有的变量和方法引用都作为符号引用(Symbilic Reference)保存在class文件的常量池里。

比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,动态链接的作用就是为了将这些符号引用转换位调用方法的直接引用

方法的调用:

在Jvm中,将符号引用转换位调用方法的直接引用,与方法的绑定机制相关。

静态链接:

        当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知,且运行期保持不变。

        将调用方法的符号引用转换为直接引用的过程称为静态链接

动态链接:

        被调用的目标方法在编译期无法被确定下来,只能够在程序运行期将方法的符号引用转换为直接引用,这种引用转换的过程具备动态性,称为动态链接

方法的绑定机制分为早期绑定(Early Binding)晚期绑定(Late Bingind)。绑定是一个字段、方法或类在符号引用被替换为直接引用的过程。

早期绑定:

被调用的目标方法在编译期可知,且运行保持不变。

晚期绑定:

        被调用方法在编译期无法被确定下来,只能够在程序运行期根据实际类型绑定相关的方法。

虚方法与非虚方法:

        如果方法在编译期就确定具体调用版本,这个版本在运行期间是不可变的。

        静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法,其它方法都为虚方法

虚拟机中方法调用指令:

普通调用指令:

1) invokestatic:调用静态方法,解析阶段确定唯一方法版本。
2) invokespecial:调用<init>方法、私有及父类方法,解析阶段确定唯一方法版本。
3) invokevirtual:调用所有虚方法。
4) invokeinterface:调用接口方法。

动态调用指令:

5) invokedynamic:动态解析出需要调用的方法,然后执行

invokeinterface:固化在虚拟机内部,方法的调用执行不可人为干预。
invokedynamic:指令支持用户确定方法版本。
invokestatic:指令和invokespecial指令调用的方法称为非虚方法,其余(final修饰除外)称为虚方法

JVM栈帧内部结构-操作数栈

基本概念:

        操作数栈是基于数组的方式实现的。

        在方法执行过程中,根据字节指令,往栈中写入(入栈/push)数据或提取(出栈/pop)数据。

        某些字节码指令将值压入操作数栈,其余的字节码指令将操作数取出栈,使用它们后再把结果压入栈。(比如:复制,交换,求和等操作)

        操作数栈主要用于保存计算过程的中间结果,同时作为计算过程中变量临时存储空间。

        操作数栈是Jvm执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的。

        每一个操作数栈都会拥有一个明确的栈深度用于存储数据,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值。
        栈中任何一个元素都是可以任意的Java数据类型
            1) 32bit的类型占用一个单位深度。
            2) 64bit的类型占用两个栈单位深度。
        操作数栈并非采用访问索引的方式进行数据访问的,只能通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问。

        如果被调用的方法带有返回值的话,其返回值会被压入当前栈帧的操作数栈中,并更新PC寄存器中下一条需要执行的字节码指令。
        
        操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,这由编译器在编译期间进行验证,同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证。

操作数栈字节码指令执行分析:

栈顶缓存(Top-Of-Stack Cashing)技术

        由于栈式架构的虚拟机所使用的零地址指令更加紧凑,但完成一项操作时,需要更多的入栈和出栈指令,所以需要更多的指令分派(instruction dispatch)次数和内存读/写次数。

        操作数是存储在内存中的,频繁执行内存读/写操作影响执行速度,HotSpot提出了栈顶缓存技术,将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读/写次数,提升执行引擎的执行效率