Java虚拟机结构(第3部分)栈帧、对象、浮点、异常

2.6 栈帧

栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。

栈帧随着方法调用而创建,随着方法结束而销毁

栈帧的存储空间分配在Java虚拟机栈之中,每一个栈帧都有自己的局部变量表(Local Variables)、操作数栈(Operand Stack)和指向当前方法所属的类的运行时常量池的引用。

栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。

2.6.1 局部变量表

存储于类和接口的二进制表示之中,既通过方法的Code属性保存及提供给栈帧使用。

局部变量使用索引来进行定位访问,第一个局部变量的索引值为零,局部变量的索引值是从零至小于局部变量表最大容量的所有整数。

当一个实例方法被调用的时候,第0个局部变量一定是用来存储被调用的实例方法所在的对象的引用(即Java语言中的“this”关键字)

2.6.2 操作数栈

每一个栈帧内部都包含一个称为操作数栈的后进先出(Last-In-First-Out,LIFO)栈

Java虚拟机提供一些字节码指令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于从操作数栈取走数据、操作数据和把操作结果重新入栈。在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果。

2.6.3 动态链接

每一个栈帧内部都包含一个指向运行时常量池的引用来支持当前方法的代码实现动态链接

在Class文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用(Symbolic Reference)来表示的,动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用

2.6.4 方法正常调用完成

方法正常完成发生在一个方法执行过程中遇到了方法返回的字节码指令的时候,使用哪种返回指令取决于方法返回值的数据类型(如果有返回值的话)。

当前栈帧承担着回复调用者状态的责任,其状态包括调用者的局部变量表、操作数栈和被正确增加过来表示执行了该方法调用指令的程序计数器等

2.6.5 方法异常调用完成

某些指令导致了Java虚拟机抛出异常,并且虚拟机抛出的异常在该方法中没有办法处理,或者在执行过程中遇到了athrow字节码指令显式地抛出异常,并且在该方法内部没有把异常捕获住

2.7 对象的表示

Java虚拟机规范不强制规定对象的内部结构应当如何表示。

2.8 浮点算法

Java虚拟机采纳了IEEE 754浮点算法规范中的部分子集。

2.8.1 Java虚拟机和IEEE 754中的浮点算法

主要差别有:

  • 在Java虚拟机中的浮点操作在遇到非法操作不会抛出exception、trap或者其他IEEE 754异常情况中定义的信号。

  • 在Java虚拟机中不支持IEEE 754中的信号浮点比较

  • 在Java虚拟机中,舍入操作永远使用IEEE 754规范中定义的向最接近数舍入模式,无法精确表示的结果将会舍入为最接近的可表示值来保证此值的最低有效位为零;浮点数转化为整型数是使用向零舍入

  • 在Java虚拟机中不支持IEEE 754的单精度扩展和双精度扩展格式

2.8.2 浮点模式

每一个方法都有一项属性称为浮点模式(Floating-Point Mode),取值有两种,FP-strict模式/非FP-strict模式。

2.8.3 数值集合转换

在一些特定场景下,支持扩展指数集合的Java虚拟机实现数值在标准浮点数集合与扩展指数集合之间的映射关系是允许和必要的,这种映射操作就称为数值集合转换。数值集合转换并非数据类型转换,而是在同一种数据类型之中不同数值集合的映射操作。

2.9 初始化方法的特殊命名

构造函数,是以一个名为的特殊实例初始化方法的形式出现的

实例初始化方法只能在实例的初始化期间,通过Java虚拟机的invokespecial指令来调用,只有在实例正在构造的时候,实例初始化方法才可以被调用访问

一个类或者接口最多可以包含不超过一个类或接口的初始化方法,类或者接口就是通过这个方法完成初始化的,一个不包含参数的静态方法,名为

类或接口的初始化方法由Java虚拟机自身隐式调用,没有任何虚拟机字节码指令可以调用这个方法,只有在类的初始化阶段中会被虚拟机自身调用。

2.10 异常

Java虚拟机里面的异常使用Throwable或其子类的实例来表示,抛异常的本质实际上是程序控制权的一种即时的、非局部(Nonlocal)的转换——从异常抛出的地方转换至处理异常的地方。

绝大多数的异常的产生都是由于当前线程执行的某个操作所导致的,这种可以称为是同步的异常。与之相对的,异步异常是指在程序的其他任意地方进行的动作而导致的异常。Java虚拟机中异常的出现总是由下面三种原因之一导致的:

  • 虚拟机同步检测到程序发生了非正常的执行情况,这时异常将会紧接着在发生非正常执行情况的字节码指令之后抛出

  • athrow字节码指令被执行。

  • 异步异常:

    • 调用了Thread或者ThreadGroup的stop方法。

    • Java虚拟机实现的内部程序错误。

《Java虚拟机规范》允许在异步异常被抛出时额外执行一小段有限的代码,允许代码优化器在不违反Java语言语义的前提下检测并把这些异常在可处理它们的地方抛出①。

抛出异常的动作在Java虚拟机之中是一种被精确定义的程序控制权转移过程。

搜索异常处理器时的搜索顺序是很关键的,在Class文件里面,每个方法的异常处理器都存储在一个表中(§4.7.3)。在运行时,当有异常出现之后,Java虚拟机就按照Class文件中的异常处理器表描述异常处理器的先后顺序,从前至后进行搜索。

说明