Java虚拟机运行时栈结构

2021/7/1 9:51:13

本文主要是介绍Java虚拟机运行时栈结构,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

  • 概述
  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法返回地址
  • 附加信息

概述

Java虚拟机以方法作为最基本的执行单元,栈帧(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的栈(Virtual Machine Stack)的栈元素。栈存储了方法局部变量表、操作数栈、动态链接和方法返回地址等信息,每一个方法从调用开始到执行结束的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。在活动的线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是有效的,其被称为“当前栈”(Current Stack Frame),与这个栈帧相关联的方法被称为“当前方法”(Current Method)。执行引擎所运行的所有自己码只对当前栈帧进行操作。

局部变量表

局部变量表是一组变量值得存储空间,用于存放方法参数和方法内部定义的局部变量。在Class文件中的Code属性的max_locals数据项中确定了该方法所需分配的局部变量的最大容量。
局部变量表是以变量槽为最小单位,《Java虚拟机规范》中只是很有向导性的说到每个变量槽都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。一个变量槽可以存放一个32位以内的数据类型。对于64位的数据类型,Java虚拟机会以高位对其的方式为其分配两个连续的变量槽空间,Java中明确的64位的数据类型只用long和double,这与“long和double的非原子协定”中允许把一次long和double类型分割2次32位读写操作有些类似。
当一个方法调用的时候,Java虚拟机会使用局部变量表来完成参数值到参数变量列表的传递过程,即实参到形参的传递过程。为了节省栈帧耗用的内存空间,局部变量表中的变量槽是可以重用的,方法体定义的变量,其作用域并不一定覆盖整个方法体,如果当前字节码PC计数器的值已经超出了某个变量的作用域,那这个变量对应的变量槽就可以交给其他变量来重用。

操作数栈

操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(Last In First Out,LIFO)栈。操作数栈的最大深度也是在编译的时候被写入到Code属性的max_stacks数据项之中。32位数据类型的所占的容量是1,64位数据类型所占的栈容量是2。
当一个方法开始执行的时候,这个方法 的操作数栈是空的,在方法执行的过程中,会用各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈的过程。操作栈中元素的数据类型必须与字节码指令的序列严格匹配。
在概念模型中,两个不同栈帧作为不同方法的虚拟机栈的元素,是完全相互独立的。但是在大多数虚拟机实现里都会做一些优化处理,令两个栈帧出现一部分的重叠,让下面栈帧的部分操作数栈与上面栈帧的部分局部变量重叠在一起,这样做不仅节约了一些空间,更重要的是进行方法调用时就可以直接公用一部分数据,必须进行额外的参数复制传递了。
Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,里面的栈就是操作数栈。

动态链接

每个栈帧都包含一个指向运行时常量池中该栈所属的方法引用,持有这个引用是为了支持方法调用过程中的动态链接(Dynamic Linking)。字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接已用,这种转化被称为静态解析,另一部分将在每一次运行期间都转化为直接引用,这部分就称为动态链接。

方法返回地址

当一个方法开始执行后,只用两种方式退出这个方法。第一种是执行引擎遇到任意一个方法返回的字节码指令,这个时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者或者主调方法),方法是否有返回值以及返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为“正常调用完成”(Normal Method Invocation Completion)。
另一种退出方式是在方法执行的过程中遇到异常,并且这个异常没有再方法体内得到妥善处理。这种退出方式称为“异常调用完成”(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者提供任何返回值。
无论采用何种方法退出,在方法退出之后,都必须返回到最初方法调用时的位置,程序才能继续执行,方法退出的时候需要在栈帧保存一些信息,用来帮助恢复它的上层主调方法的还行状态。一般来说,方法正常退出的时候,主调方法的PC计数器的可以作为返回地址,栈中可能保存这个计数器的值。而方法异常退出的时候,返回地址需要通过异常处理器表来确定的,栈中一般不会保存这部分信息。
方法退出的过程实际上等同于把当前栈出栈,因此退出的时候可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。

附加信息

《Java虚拟机规范》允许虚拟机实现增加一些规范里没有描述的信息到站之中,例如与调试、性能收集相关的信息,这部分信息完全取决于虚拟机的实现。



这篇关于Java虚拟机运行时栈结构的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程