深入理解JVM(一):什么是JVM?什么是JVM内存模型?( 三 )

操作数栈

  • 操作数栈也常被称为操作栈 , 它是一个先进后出栈 。
  • 操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中 。
  • 操作数栈的每一个元素都可以是包括long和double在内的任意Java数据类型 。 32位数据类型所占的栈容量为1 , 64位数据类型所占的栈容量为2 。
  • 方法刚刚开始执行的时候 , 这个方法的操作数栈是空的 , 在方法的执行过程中 , 会有各种字节码指令往操作数栈中写入和提取内容 , 也就是出栈和入栈操作 。
  • 操作数栈中元素的数据类型必须与字节码指令的序列严格匹配 , 例如iadd指令 , 不能出现一个long和一个float使用iadd命令相加的情况 。
动态连接
  • 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用 , 持有这个引用是为了支持方法调用过程中的动态连接 。
  • Class文件的常量池中存有大量的符号引用 , 字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数 。 这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用 , 这种转化被称为静态解析 。 另外一部分将在每一次运行期间都转化为直接引用 , 这部分就称为动态连接 。
方法出口
  • 当一个方法开始执行后 , 只有两种方式退出这个方法 。
  • 第一种方式是执行引擎遇到任意一个方法返回的字节码指令 , 这时候可能会有返回值传递给上层的方法调用者 , 方法是否有返回值以及返回值的类型将根据遇到何种方法返回指令来决定 , 这种退出方法的方式称为“正常调用完成” 。
  • 另外一种退出方式是在方法执行的过程中遇到了异常 , 并且这个异常没有在方法体内得到妥善处理 。 无论是Java虚拟机内部产生的异常 , 还是代码中使用throw字节码指令产生的异常 , 只要在本方法的异常表中没有搜索到匹配的异常处理器 , 就会导致方法退出 , 这种退出方法的方式称为“异常调用完成” 。 这种方法的返回是不会给它的上层调用者提供任何返回值的 。
  • 无论采用何种退出方式 , 在方法退出之后 , 都必须返回到最初方法被调用时的位置 , 程序才能继续执行 , 方法返回时可能需要在栈帧中保存一些信息 , 用来帮助恢复它的上层主调方法的执行状态 。
  • 方法退出的过程实际上等同于把当前栈帧出栈 , 因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈 , 把返回值(如果有的话)压入调用者栈帧的操作数栈中 , 调整PC计数器的值以指向方法调用指令后面的一条指令等 。
图解
? 以 int i = 1; 这样代码为例 , 看看虚拟机栈的执行