JVM 栈桢

 

栈帧中存储的有
  • 局部变量表
  • 操作数栈(或表达式栈)
  • 动态链接(或指向运行时常量池的方法引用)
  • 方法返回地址(或方法正常退出时或者异常退出时的定义)
  • 一些附加信息

 

一、局部变量表
是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在 Java 程序编译为 Class 文件时,就在方法的 Code 属性的 max_locals 数据项中确定了该方法所需要分配的局部变量表的最大容量。
  • 局部变量表最基本的存储单元是Slot(变量槽)
  • 局部变量表中存放编译期可知的各种基本数据类型(8种)、引用类型(reference),returnAddress类型
  • 局部变量表里,32位以内的类型只占用一个slot(包括returnAddress),64位的类型(long和double)占用2个Slot
  • byte、short、char 在存储前被转换为 int,boolean也被转换为int,0表示false,非0表示true。
  • 局部变量表也被称之为局部变量数组或本地变量表
  • 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables 数据项中。在方法运行期间是不会改变局部变量表的大小的

 

通过命令:javap -verbose 执行.class文件

 

二、操作数栈
栈可以使用数组或链表来实现。操作数栈也称之为表达式栈

  • 操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
  • 操作数栈就是JVM执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的。
  • 每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值。
  • 栈中的任何一个元素都是可以任意的Java数据类型。
32bit的类型占用一个栈单位深度
64bit的类型占用一个栈单位深度
  • 操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈和出栈操作来完成一次数据访问。
 
在方法的执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈/出栈
某些字节码指令将值压入操作数栈,其余的字节码指令将操作数取出栈。使用它们后再把结果压入栈。

 

示例

public void testAddOperation() {
    byte i = 15;
    int j = 8;
    int k = i + j;
}
// 对应code,即字节码指令
 0 bipush 15
 2 istore_1
 3 bipush 8
 5 istore_2
 6 iload_1
 7 iload_2
 8 iadd
 9 istore_3
10 return

执行步骤

 

 

三、动态链接
动态链接也称之为指向运行时常量池的方法引用
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking) 比如:invokeddynamic指令。
在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Refrence)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
 

 

四、方法返回地址
  • 存放调用该方法的pc寄存器的值
  • 一个方法的结束,有两种方式:正常执行完成;出现未处理的异常,非正常退出。
  • 无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
 
本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。
正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给它的上层调用者产生任何的返回值。
 
当一个方法开始执行后,只有两种方式可以退出这个方法:
1. 执行引擎遇到任意一个方法返的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口;
    一个方法在正常调用完成之后究竟需要使用哪一个返回指令还需要根据方法返回值的实际数据类型而定。
    在字节码指令中,返回指令包含ireturn(当返回值是boolean、byte、char、short和int类型时)、lretrun、freturn、dreturn以及areturn,另外有一个return指令供声明为void的方法、实例初始化方法、类和接口的初始化方法使用。
2. 在方法执行的过程中遇到了异常,并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出。简称异常完成出口。

 

posted @ 2020-04-20 04:20  cao_xiaobo  阅读(262)  评论(0编辑  收藏  举报