Java 虚拟机结构 (4)

在一条线程之中,只有目前正在执行的那个方法的栈帧是活动的。这个栈帧就被称为是当前栈 帧(Current Frame),这个栈帧对应的方法就被称为是当前方法(Current Method),定义 这个方法的类就称作当前类(Current Class)。对局部变量表和操作数栈的各种操作,通常都 指的是对当前栈帧的对局部变量表和操作数栈进行的操作。

如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧 了。当一个新的方法被调用,一个新的栈帧也会随之而创建,并且随着程序控制权移交到新的方法 而成为新的当前栈帧。当方法返回的之际,当前栈帧会传回此方法的执行结果给前一个栈帧,在方法返回之后,当前栈帧就随之被丢弃,前一个栈帧就重新成为当前栈帧了。

请读者特别注意,栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。 

5.1 局部变量表

每个栈帧内部都包含一组称为局部变量表(Local Variables)的变量列表。栈帧中局部变量表的长度由编译期决定,并且存储于类和接口的二进制表示之中,既通过方法的 Code 属性保存及提供给栈帧使用。 一个局部变量可以保存一个类型为 boolean、byte、char、short、float、reference 和 returnAddress 的数据,两个局部变量可以保存一个类型为 long 和 double 的数据。 局部变量使用索引来进行定位访问,第一个局部变量的索引值为零,局部变量的索引值是从零 至小于局部变量表最大容量的所有整数。 long 和 double 类型的数据占用两个连续的局部变量,这两种类型的数据值采用两个局部变 量之中较小的索引值来定位。例如我们讲一个 double 类型的值存储在索引值为 n 的局部变量中, 实际上的意思是索引值为 n 和 n+1 的两个局部变量都用来存储这个值。索引值为 n+1 的局部变量 是无法直接读取的,但是可能会被写入,不过如果进行了这种操作,就将会导致局部变量 n 的内 容失效掉。

上文中提及的局部变量 n 的 n 值并不要求一定是偶数,Java 虚拟机也不要求 double 和 long 类型数据采用 64 位对其的方式存放在连续的局部变量中。虚拟机实现者可以自由地选择适当的方 式,通过两个局部变量来存储一个 double 或 long 类型的值。

Java 虚拟机使用局部变量表来完成方法调用时的参数传递,当一个方法被调用的时候,它的 参数将会传递至从 0 开始的连续的局部变量表位置上。特别地,当一个实例方法被调用的时候, 第 0 个局部变量一定是用来存储被调用的实例方法所在的对象的引用(即 Java 语言中的“this” 关键字)。后续的其他参数将会传递至从 1 开始的连续的局部变量表位置上。

5.2 操作数栈

每一个栈帧(§2.6)内部都包含一个称为操作数栈(Operand Stack)的后进先出 (Last-In-First-Out,LIFO)栈。栈帧中操作数栈的长度由编译期决定,并且存储于类和接 口的二进制表示之中,既通过方法的 Code 属性(§4.7.3)保存及提供给栈帧使用。 在上下文明确,不会产生误解的前提下,我们经常把“当前栈帧的操作数栈”直接简称为“操 作数栈”。 操作数栈所属的栈帧在刚刚被创建的时候,操作数栈是空的。Java 虚拟机提供一些字节码指 令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于 从操作数栈取走数据、操作数据和把操作结果重新入栈。在方法调用的时候,操作数栈也用来准备 调用方法的参数以及接收方法返回结果。

举个例子,iadd 字节码指令的作用是将两个 int 类型的数值相加,它要求在执行的之前操作 数栈的栈顶已经存在两个由前面其他指令放入的 int 型数值。在 iadd 指令执行时,2 个 int 值 从操作栈中出栈,相加求和,然后将求和结果重新入栈。在操作数栈中,一项运算常由多个子运算 (Subcomputations)嵌套进行,一个子运算过程的结果可以被其他外围运算所使用。

每一个操作数栈的成员(Entry)可以保存一个 Java 虚拟机中定义的任意数据类型的值,包 括 long 和 double 类型。

在操作数栈中的数据必须被正确地操作,这里正确操作是指对操作数栈的操作必须与操作数栈 栈顶的数据类型相匹配,例如不可以入栈两个 int 类型的数据,然后当作 long 类型去操作他们, 或者入栈两个 float 类型的数据,然后使用 iadd 指令去对它们进行求和。有一小部分 Java 虚拟机指令(例如 dup 和 swap 指令)可以不关注操作数的具体数据类型,把所有在运行时数据区 中的数据当作裸类型(Raw Type)数据来操作,这些指令不可以用来修改数据,也不可以拆散那 些原本不可拆分的数据,这些操作的正确性将会通过 Class 文件的校验过程来强制保 障。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwsgy.html