第2章Java虚拟机的结构
上一页 下一个

第2章Java虚拟机的结构

这个文件指定了一个抽象机器。 它没有描述Java虚拟机的任何特定的实现。

要正确实现Java虚拟机,只需要能够读取class文件格式并正确执行其中指定的操作即可。 不属于Java虚拟机规范的实现细节将不必要地限制实现者的创造力。 例如,运行时数据区域的内存布局,所使用的垃圾收集算法以及Java虚拟机指令的任何内部优化(例如将它们转换为机器代码)均由实现者自行决定。

在本规范中对Unicode的所有引用都是针对可在http://www.unicode.org/获得的Unicode标准6.0.0版给出的。

2.1。 class文件格式

由Java虚拟机执行的编译代码使用与硬件和操作系统无关的二进制格式表示,典型地(但不一定)存储在被称为class文件格式的文件中。 class文件格式精确地定义了一个类或接口的表示形式,其中包括诸如字节排序等细节,这些信息可能被认为是特定于平台的对象文件格式。

第4章“ class文件格式”详细介绍了class文件格式。

2.2。 数据类型

与Java编程语言一样,Java虚拟机操作两种类型: 基本类型引用类型 。 相应地,有两种类型的值可以存储在变量中,作为参数传递,通过方法返回,并在原始值参考值上进行操作 。

Java虚拟机希望几乎所有的类型检查都是在运行时间之前完成的,通常由编译器来完成,而不必由Java虚拟机本身完成。 原始类型的值不需要被标记或以其他方式被检查以在运行时确定它们的类型,或者与参考类型的值区分。 相反,Java虚拟机的指令集使用指令操作特定类型的值来区分其操作数类型。 例如, iadd , ladd , fadddadd都是Java虚拟机指令,它们会添加两个数值并生成数值结果,但每个指令都分别专用于其操作数类型: int , long , floatdouble 。 有关Java虚拟机指令集中类型支持的概述,请参阅§2.11.1 。

Java虚拟机包含对对象的明确支持。 一个对象是一个动态分配的类实例或一个数组。 对对象的引用被认为具有Java虚拟机类型reference 。 类型reference值可以被看作是指向对象的指针。可能存在对一个对象的多个引用。 对象总是在类型reference值上运行,传递和测试。

2.3。 原始类型和值

Java虚拟机支持的基本数据类型是数字类型 , boolean类型( §2.3.4 )和returnAddress类型( §2.3.3 )。

数字类型由整型 ( §2.3.1 )和浮点型 ( §2.3.2 )组成。

整数类型是:

  • byte ,其值是8位有符号的二进制补码整数,其默认值为零

  • short ,其值是16位有符号的二进制补码整数,其缺省值为零

  • int ,其值是32位有符号二进制补码整数,其默认值为零

  • long ,其值是64位有符号二进制补码整数,其默认值为零

  • char ,其值为16位无符号整数,表示基本多语言平面中的Unicode编码点,用UTF-16编码,默认值为空代码点( '\u0000' )

浮点类型是:

  • float ,其值是浮点值集合的元素,或者在支持的情况下,浮点扩展指数值集合,其默认值为正零

  • double ,它们的值是double值集合的元素,或者在支持的情况下是double-extended-exponent值集合,并且其默认值为正值

boolean类型的值将真值truefalse进行编码,默认值为false 。

Java®虚拟机规范的第一版没有将boolean视为Java虚拟机类型。 但是, boolean值在Java虚拟机中的支持有限。 TheJava®Virtual Machine Specification的第二版通过将boolean类型视为一种类型来阐明了这个问题。

returnAddress类型的值是指向Java虚拟机指令操作码的指针。 在原始类型中,只有returnAddress类型不直接与Java编程语言类型相关联。

2.3.1。 积分类型和值

Java虚拟机的整体类型的值是:

  • 对于byte ,从-128到127(-2 7到2 7 - 1)(含)

  • short ,从-32768到32767(-2 15到2 15 - 1)(含)

  • 对于int ,从-2147483648到2147483647(-2 31到2 31 - 1)(含)

  • long ,从-9223372036854775808到9223372036854775807(-263到2 63-1)

  • 对于char ,从0到65535(含)

2.3.2。 浮点类型,值集和值

浮点类型是floatdouble ,它们在概念上与32位单精度和64位双精度格式的IEEE 754值和在IEEE标准中针对二进制浮点运算 (ANSI / IEEE标准754-1985,纽约)。

IEEE 754标准不仅包括正负符号幅度值,还包括正负的零,正负的无穷大和一个特殊的非数值(以下简称“NaN”)。 NaN值用于表示某些无效操作的结果,例如零除零。

Java虚拟机的每个实现都需要支持两组浮点值,称为浮点值集双值集 。 另外,Java虚拟机的实现可以选择支持两个扩展指数浮点值集中的一个或两个,称为浮点扩展指数值集双扩展指数值集。 在某些情况下,可以使用这些扩展指数值集而不是标准值集来表示floatdouble类型的值。

任何浮点值集合的有限非零值都可以表示为s · m · 2 ( eN + 1) ,其中s是+1或-1, m是小于2N的正整数, emin = - (2 K -1 -2)和max = 2 K -1 -1(含)之间的整数,并且其中NK是取决于所设置的值的参数。 有些价值观可以用不止一种方式来表示, 例如,假设一个值集合中的一个值v可以用s , me的某些值以这种形式表示,那么如果m发生偶然和e小于2 K -1 , m并将e增加1以产生相同值v的第二表示。 如果m≥2N -1 ,则将这种形式的表示称为归一化 ; 否则表示被称为非规范化 。 如果一个值集合中的值不能以m≥2N -1的方式表示,那么这个值就被认为是一个非规格化的值 ,因为它没有规范化的表示。

表2.3.2-A总结了两个必需的和两个可选浮点值集合的参数NK (以及导出参数minmax )的约束条件。

表2.3.2-A。 浮点值设置参数

参数 浮动 浮扩展-指数 双扩展指数
ñ 24 24 53 53
ķ 8 ≥11 11 ≥15
最大 +127 ≥+ 1023 +1023 ≥ + 16383
分钟 -126 ≤- 1022 -1022 ≤- 16382

在一个实现支持一个或两个扩展指数值集合的情况下,对于每个支持的扩展指数值集合,都有一个具体的实现相关常量K ,其值受表2.3.2-A的限制; 这个值K反过来决定minmax的值 。

四个数值集中的每一个都不仅包含上面描述的有限非零值,还包括正数零,负数零,正无穷大,负数无穷大和NaN五个值。

请注意, 表2.3.2-A中的约束被设计为使得浮点值集合的每个元素都必然也是浮点扩展指数值集合的一个元素,双倍值集合和双倍扩展指数值组。 同样,双值集合的每个元素也必然是双重扩展指数值集合的元素。 每个扩展指数值集具有比相应的标准值集更大的指数值范围,但没有更高的精度。

除了只有一个NaN值(IEEE 754指定2 24 -2个不同的NaN值)之外,浮点值集的元素正好是可以使用IEEE 754标准中定义的单个浮点格式表示的值。 除了只有一个NaN值(IEEE 754指定了2 53-2个不同的NaN值)之外,double值集合的元素正好是可以使用IEEE 754标准中定义的双重浮点格式表示的值。 但是请注意,这里定义的浮点扩展指数和双扩展指数值集的元素并不分别对应于可以使用IEEE 754单扩展和双扩展格式表示的值。 除非浮点值必须以class文件格式( §4.4.4 , §4.4.5 )表示,否则本规范不要求对浮点值集的值进行特定表示。

float,float-extended-exponent,double和double-extended-exponent值集不是类型。 Java虚拟机的实现使用浮点值集的一个元素来表示float类型的值总是正确的; 但是,在某些情况下,实现可能允许使用float-extended-exponent值集的一个元素。 类似地,对于一个实现来说,使用double值集的一个元素来表示double类型的值总是正确的; 然而,在某些情况下,实现可能允许使用双扩展指数值集的元素。

除NaN之外,浮点值集的值是有序的 。 当从最小到最大排列时,它们是负无穷,负有限值,正负零,正有限值和正无穷大。

浮点正零和浮点负零比较相等,但还有其他的操作可以区分它们; 例如,将1.0除以0.0产生正无穷大,但将1.0除以-0.0会产生负无穷大。

NaN是无序的 ,所以数值比较的数值比较和测试的值都是false如果它们中的任何一个或两个都是NaN。 特别是,当且仅当该值为NaN时,对值的数值相等的测试才具有值false 。 对于数字不等式的测试,如果任一操作数是NaN,则其值为true 。

2.3.3。 returnAddress类型和值

returnAddress类型由Java虚拟机的jsr , retjsr_w指令使用( §jsr , §ret , §jsr_w )。 returnAddress类型的值是指向Java虚拟机指令操作码的指针。 与数字基元类型不同, returnAddress类型不对应于任何Java编程语言类型,并且不能由正在运行的程序修改。

2.3.4。 boolean类型

尽管Java虚拟机定义了一个boolean类型,但它只能提供非常有限的支持。 没有专用于boolean值操作的Java虚拟机指令。 相反,Java编程语言中操作boolean值的表达式被编译为使用Java虚拟机的int数据类型的值。

Java虚拟机直接支持boolean数组。 它的newarray指令( §newarray )可以创建boolean数组。 使用byte数组指令baloadbastore ( §baload , §bastore )访问和修改boolean类型的数组。

在Oracle的Java虚拟机实现中,Java编程语言中的boolean数组被编码为Java虚拟机byte数组,每个boolean元素使用8位。

Java虚拟机使用1来表示true0来表示false来对boolean数组组件进行编码。 在Java编程语言boolean值被编译器映射到Java虚拟机类型int值的地方,编译器必须使用相同的编码。

2.4。 参考类型和值

有三种reference类型:类类型,数组类型和接口类型。 它们的值分别是对动态创建的类实例,数组,或实现接口的类实例或数组的引用。

数组类型由具有单个维度的组件类型 (其长度不由该类型给出)组成。 数组类型的组件类型本身可以是数组类型。 如果从任何数组类型开始,考虑它的组件类型,然后(如果这也是一个数组类型)该类型的组件类型等等,最终必须达到不是数组类型的组件类型; 这被称为数组类型的元素类型。 数组类型的元素类型必须是原始类型,类类型或接口类型。

一个reference值也可以是特殊的空引用,对没有对象的引用,这里用null表示。 null引用最初没有运行时类型,但可以转换为任何类型。 reference类型的默认值为null 。

本规范不要求编码为null的具体值。

2.5。 运行时数据区

Java虚拟机定义了程序执行期间使用的各种运行时数据区域。 其中一些数据区域是在Java虚拟机启动时创建的,并且只有在Java虚拟机退出时才会被销毁。 其他数据区域是每个线程。 每个线程数据区域在线程退出时创建和销毁时创建。

2.5.1。 pc注册

Java虚拟机可以同时支持多个执行线程(JLS§17)。 每个Java虚拟机线程都有自己的pc (程序计数器)寄存器。 在任何时候,每个Java虚拟机线程正在执行单个方法的代码,即该线程的当前方法( §2.6 )。 如果该方法不是native ,则pc寄存器包含当前正在执行的Java虚拟机指令的地址。 如果线程当前正在执行的方法是native ,那么Java虚拟机的pc寄存器的值是未定义的。 Java虚拟机的pc寄存器足够宽以在特定平台上保存returnAddress或本机指针。

2.5.2。 Java虚拟机堆栈

每个Java虚拟机线程都有一个私有Java虚拟机堆栈 ,与线程同时创建。 Java虚拟机堆栈存储帧(第2.6节 )。 Java虚拟机堆栈类似于传统语言(如C语言)的堆栈:它包含局部变量和部分结果,并在方法调用和返回中起作用。 由于除了推送和弹出帧之外,Java虚拟机堆栈不会被直接操作,因此可能会分配堆栈。 Java虚拟机堆栈的内存不需要是连续的。

Java®虚拟机规范的第一版中,Java虚拟机堆栈被称为Java堆栈 。

这个规范允许Java虚拟机栈或者是固定的大小,或者是按照计算的需要动态扩展和收缩。 如果Java虚拟机堆栈的大小是固定的,则每个Java虚拟机堆栈的大小可以在创建堆栈时独立选择。

Java虚拟机实现可以提供程序员或用户控制Java虚拟机堆栈的初始大小,以及在动态扩展或收缩Java虚拟机堆栈的情况下控制最大和最小大小。

以下异常情况与Java虚拟机堆栈相关联:

  • 如果线程中的计算需要比允许的更大的Java虚拟机堆栈,则Java虚拟机会引发StackOverflowError 。

  • 如果可以动态扩展Java虚拟机堆栈并尝试扩展,但是没有足够的内存可用于扩展,或者如果没有足够的内存可用于为新线程创建初始Java虚拟机堆栈,则Java虚拟机机器抛出一个OutOfMemoryError 。

2.5.3。 堆

Java虚拟机具有在所有Java虚拟机线程中共享的 。 堆是所有类实例和数组的内存分配的运行时数据区。

堆是在虚拟机启动时创建的。 对象的堆存储由自动存储管理系统(称为垃圾收集器 ) 回收 ; 对象从不显式释放。 Java虚拟机假定没有特定类型的自动存储管理系统,并且存储管理技术可以根据实现者的系统要求来选择。 堆可以是固定的大小,或者可以根据计算的需要而扩展,并且如果不需要更大的堆,那么堆可以被缩小。 堆的内存不需要是连续的。

Java虚拟机实现可以提供程序员或用户控制堆的初始大小,以及如果堆可以动态扩展或收缩,则控制最大和最小堆大小。

以下异常情况与堆相关联:

  • 如果计算需要比自动存储管理系统可用的更多的堆,那么Java虚拟机会引发OutOfMemoryError 。

2.5.4。 方法区域

Java虚拟机具有在所有Java虚拟机线程之间共享的方法区域 。 方法区域类似于常规语言的编译代码的存储区域,或类似于操作系统进程中的“文本”段。 它存储每个类的结构,如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化和接口初始化的特殊方法( §2.9 )。

方法区域是在虚拟机启动时创建的。 尽管方法区域在逻辑上是堆的一部分,但是简单的实现可以选择不垃圾收集或压缩它。 本规范不要求方法区域的位置或用于管理编译代码的策略。 方法区域可以具有固定的大小,或者可以根据计算的需要进行扩展,并且如果更大的方法区域变得不必要,则可以缩小方法区域。 方法区域的内存不需要是连续的。

Java虚拟机实现可以提供程序员或用户对方法区域的初始大小的控制,以及在变大小方法区域的情况下控制最大和最小方法区域大小。

以下例外情况与方法区域相关联:

  • 如果方法区域中的内存无法满足分配请求,则Java虚拟机将引发OutOfMemoryError 。

2.5.5。 运行时常量池

运行时常量池class文件( §4.4 )中constant_pool表的每类或每界面运行时表示。 它包含几种常量,从编译时已知的数字文字到必须在运行时解析的方法和字段引用。 运行时常量池提供的功能类似于传统编程语言符号表的功能,虽然它包含比典型符号表更广泛的数据范围。

每个运行时常量池都是从Java虚拟机的方法区域( §2.5.4 )分配的。 类或接口的运行时常量池是在Java虚拟机创建类或接口( §5.3 )时构建的。

以下异常情况与类或接口的运行时常量池的构造相关联:

  • 在创建类或接口时,如果运行时常量池的构造需要比Java虚拟机的方法区域更多的内存,则Java虚拟机会引发OutOfMemoryError 。

有关构建运行时常量池的信息请参阅第5节( 加载,链接和初始化 ) 。

2.5.6。 本地方法堆栈

Java虚拟机的实现可以使用俗称的“C堆栈”来支持native方法(用Java编程语言以外的语言编写的方法)的传统堆栈。 原生方法堆栈也可以通过为诸如C语言的Java虚拟机的指令集执行解释器来使用。不能加载native方法并且本身不依赖于传统堆栈的Java虚拟机实现不需要提供本地方法栈。 如果提供,则在创建每个线程时,通常为每个线程分配本地方法堆栈。

该规范允许本地方法栈或者具有固定的大小,或者根据计算的需要动态扩展和收缩。 如果本地方法堆栈的大小是固定的,那么当创建堆栈时,每个本地方法堆栈的大小可以独立选择。

Java虚拟机实现可以提供程序员或用户对本地方法堆栈的初始大小的控制,以及在大小变化的本地方法堆栈的情况下控制最大和最小方法堆栈大小。

以下异常情况与本机方法堆栈相关联:

  • 如果线程中的计算需要比允许的更大的本机方法堆栈,则Java虚拟机将抛出StackOverflowError 。

  • 如果可以动态扩展本机方法堆栈,并尝试本机方法堆栈扩展,但是没有足够的内存可用,或者如果没有足够的内存可用于为新线程创建初始本机方法堆栈,Java虚拟机将抛出OutOfMemoryError 。


2.6。 框架

一个框架用于存储数据和部分结果,以及执行动态链接,返回方法的值和调度异常。

每次调用方法时都会创建一个新的框架。 当方法调用完成时,框架被销毁,无论这个完成是正常的还是突然的(它引发一个未捕获的异常)。 帧是从创建帧的线程的Java虚拟机堆栈(第2.5.2节)分配的。 每个帧都有自己的局部变量数组( §2.6.1 ),自己的操作数栈( §2.6.2 ),以及对当前方法的类的运行时常量池( §2.5.5 )的引用。

一个框架可以用额外的特定于实现的信息来扩展,比如调试信息。

局部变量数组和操作数栈的大小在编译时确定,并与框架相关的方法( §4.7.3 )一起提供。 因此,框架数据结构的大小仅取决于Java虚拟机的实现,并且可以在方法调用时同时分配这些结构的内存。

只有一个框架,执行方法的框架,在给定的控制线程中的任何点处都是活动的。 该帧被称为当前帧 ,其方法被称为当前方法 。 当前方法定义的当前类 。 本地变量和操作数栈的操作通常是参考当前帧。

如果一个框架的方法调用另一个方法或者其方法完成,框架将不再是最新的。 当一个方法被调用时,一个新的框架被创建,并在控制转移到新的方法时变成当前的。 在方法返回时,当前帧将其方法调用的结果(如果有)返回到前一帧。 当前帧随着前一帧变成当前帧而被丢弃。

请注意,由线程创建的框架对于该线程是本地的,并且不能被任何其他线程引用。

2.6.1。 局部变量

每个帧( §2.6 )都包含一个称为局部变量的变量数组 。 帧的局部变量数组的长度在编译时确定,并以类或接口的二进制表示形式提供,以及与帧相关联的方法的代码(第4.7.3节 )。

一个局部变量可以包含boolean , byte , char , short , int , float , referencereturnAddress类型的boolean 。 一对局部变量可以保存longdouble类型的值。

局部变量通过索引来解决。 第一个局部变量的索引是零。 一个整数被认为是局部变量数组的一个索引,当且仅当该整数在0到1之间,小于局部变量数组的大小。

long类型或double类型的值占用两个连续的局部变量。 这样的价值只能用较小的指数来解决。 例如,在索引为n的局部变量数组中存储的double类型的值实际上占据了索引为nn + 1的局部变量; 但是,在索引n +1的本地变量不能从中加载。 它可以存储进去。 但是,这样做会使局部变量n的内容无效。

Java虚拟机不要求n是偶数。 直观地说,类型longdouble值在局部变量数组中不需要64位对齐。 实现者可以自由决定使用为该值保留的两个局部变量来表示这些值的适当方式。

Java虚拟机使用局部变量在方法调用上传递参数。 在类方法调用中,任何参数都是从本地变量0开始的连续局部变量中传递的。 在实例方法调用中,总是使用局部变量0将引用传递给调用实例方法的对象(在Java编程语言中)。 随后从局部变量1开始,连续的局部变量传递任何参数。

2.6.2。 操作数堆栈

每个帧( §2.6 )都包含一个名为操作数堆栈的后进先出(LIFO) 堆栈 。 一个帧的操作数堆栈的最大深度是在编译时确定的,并与框架相关的方法的代码一起提供( §4.7.3 )。

在上下文中清楚的地方,我们有时会将当前帧的操作数堆栈简称为操作数堆栈。

当包含它的帧被创建时,操作数栈是空的。 Java虚拟机提供指令将常量或值从本地变量或字段加载到操作数堆栈中。 其他Java虚拟机指令从操作数堆栈中取操作数,对它们进行操作,并将结果返回到操作数堆栈中。 操作数堆栈也用于准备参数传递给方法和接收方法结果。

例如, iadd指令( §iadd )将两个int值一起添加。 它要求将被添加的int值作为操作数堆栈的前两个值,由前面的指令推送。 这两个int值都从操作数堆栈中弹出。 他们被添加,他们的总和被推回到操作数堆栈。 子计算可以嵌套在操作数堆栈上,从而得到可由包含计算使用的值。

操作数堆栈中的每个条目都可以包含任何Java虚拟机类型的值,包括long类型或double类型的值。

操作数堆栈中的值必须以适合其类型的方式进行操作。 例如,推动两个int值,然后把它们作为一个long int或推入两个float值,然后用iadd指令添加它们是不可能的 。 少量的Java虚拟机指令( dup指令( dup )和swap ( swap ))在运行时数据区域以原始值运行,而不考虑它们的具体类型; 这些指令的定义是不能用来修改或分解个别值的。在操作数堆栈操作这些限制是通过强制执行class文件验证(§4.10)。

在任何时间点,操作数堆栈具有相关联的深度,其中类型的值longdouble有助于两个单元的深度和任何其他类型的值贡献一个单元。

2.6.3。 动态链接

每个帧(§2.6)包含对运行时间常量池(基准2.5.5节当前方法的类型来支持)的动态链接的方法的代码。的class一种方法文件代码指的是被调用的方法和通过符号引用将被访问的变量。动态链接转换这些符号方法引用到具体的方法的引用,根据需要加载类来解决的尚未未定义的符号,并将其转换可变访问到与这些变量的运行时间的位置相关联的存储结构适当地偏移。

这晚的方法和变量的结合使得该方法使用不太可能打破这种代码的其它类的变化。

2.6.4。正常的方法调用完成

一种方法调用正常完成,如果该调用不会引起异常(§2.10)被抛出,直接从Java虚拟机或执行一个明确的结果throw声明。如果当前方法的调用正常完成,则一个值可被返回给调用方法。这发生在所调用的方法执行的返回指令(一个§2.11.8),用于被返回的值的类型(如果有的话)的选择,其必须是适当的。

当前帧(§2.6)在这种情况下用于恢复调用,包括其局部变量和操作数堆栈的状态时,具有适当递增以跳过方法调用指令调用程序的程序计数器。然后执行在所述调用方法的使用返回的值(如果有的话)帧正常继续推到该帧的操作数堆栈。

2.6.5。突然的方法调用完成

一种方法调用完成突然,如果该方法中的Java虚拟机指令的执行引起的Java虚拟机抛出一个异常(§2.10),以及异常不是方法中进行处理。一个执行athrow指令(§ athrow)也导致异常被抛出明确,如果该异常没有被当前的方法抓住了,结果突然的方法调用完成。这样就完成了突然从来没有一个方法调用返回一个值,以它的调用。

2.7。 对象的表示

Java虚拟机并不要求对对象的任何特定的内部结构。

在一些Java虚拟机的Oracle的实施方式中的,一类实例的引用是一个指针指向一个手柄,其本身一对指针:一个用于包含对象的方法和一个指向一个表Class,它表示该对象型的对象,而另一个从堆中的对象数据分配的内存。

2.8。浮点运算

Java虚拟机包含在指定的浮点运算的一个子集IEEE二进制浮点运算(ANSI / IEEE标准754-1985,纽约)。

2.8.1。Java虚拟机浮点运算和IEEE 754

由Java虚拟机和IEEE 754标准支持的浮点运算之间的主要区别是:

  • Java虚拟机的浮点运算不会抛出异常,陷阱,或以其他方式零,溢出,下溢,或不准确的信号无效操作,除法的IEEE 754得天独厚的条件。Java虚拟机没有信号NaN的价值。

  • Java虚拟机不支持IEEE 754信令浮点比较。

  • Java虚拟机的取整运算总是使用IEEE 754向最接近模式。不精确结果四舍五入到最接近的可表示值,与领带要以零最低显著位值。这是IEEE 754默认模式。但是,浮点类型的值转换为整型值的Java虚拟机指令圆趋向于零。Java虚拟机不给任何方式改变浮点舍入模式。

  • Java虚拟机不支持任何的IEEE 754单扩展,双扩展格式,但只要可以说双和双扩展,指数值设置为支持单扩展格式。浮子-扩展指数和扩展双 - 指数值集,其可任选被支持,不对应于的IEEE 754扩展格式的值:该IEEE 754扩展格式需要延长的精度以及延长指数范围。

2.8.2。浮点模式

每一种方法都有一个浮点模式,其是FP-严格不FP-严格。的方法的浮点模式由设置决定ACC_STRICT所述的标志access_flags的项的method_info结构(§4.6)定义方法。指此标志被设置的方法是FP-严格; 否则,该方法是不FP-严格。

请注意,这个映射ACC_STRICT标志意味着,在JDK编译器编译的类中的方法释放1.1或更早版本都不能有效FP-严格。

我们将把一个操作数堆栈作为具有给定的浮点模式时,其调用的方法创建包含操作数堆栈具有浮点模式的帧。同样,我们将把Java虚拟机指令为具有给定的浮点模式,当包含指令,该方法具有浮点模式。

如果浮子扩展的指数值组被支撑(§2.3.2),类型的值float不是FP-严格的操作数堆栈上的范围可以超过该值设置除非由值集转换禁止(§2.8.3) 。如果双延长的指数值组被支撑(§2.3.2),类型的值double的操作数堆栈不是FP-严格上的范围可以超过该值设置除非由值集转换禁止。

在所有其他情况下,无论操作数堆栈上或其它地方,也不管浮点模式,键入的浮点值float以及double可以仅在范围内浮动值设置和双值集合,分别。特别地,类和实例字段,数组元素,局部变量和方法参数可以仅包含从标准值集合得出的值。

2.8.3。值集转换

支持扩展的浮点值集合中的Java虚拟机的实现被允许或要求,在特定情况下,以映射的扩展和标准值集合之间的相关联的浮点类型的值。这样的值来设置转换不是一个类型的转换,但具有相同的类型相关联的值集之间的映射。

其中值集的转换被指示时,一种实现被允许执行对某个值以下操作之一:

  • 如果该值是类型的float,而不是浮点值集合的一个元素,将其映射的值到浮子值集的最近的元件。

  • 如果该值是类型的double并且不是双值集合的一个元素,将其映射中的值与双值集的最近的元件。

另外,当值设定的转换被指示时,需要某些操作:

  • Java虚拟机指令是不是FP-严格的假设执行致使类型的值float,以将其推入操作数堆栈是FP-严格,作为参数传递,或者存储到局部变量,字段,或元素的阵列。如果该值不是浮点值集合的一个元素,将其映射的值到浮子值集的最近的元件。

  • Java虚拟机指令是不是FP-严格的假设执行致使类型的值double,以将其推入操作数堆栈是FP-严格,作为参数传递,或者存储到局部变量,字段,或元素的阵列。如果该值不是双值集合的一个元素,将其映射中的值与双值集的最近的元件。

Such required value set conversions may occur as a result of passing a parameter of a floating-point type during method invocation, including native method invocation; returning a value of a floating-point type from a method that is not FP-strict to a method that is FP-strict; or storing a value of a floating-point type into a local variable, a field, or an array in a method that is not FP-strict.

不从一个扩展的指数值集合中的所有值可以准确地映射到对应的标准值集合的值。如果正被映射到的值过大,无法精确表示(其指数比由标准值集合允许更大的),它被转换为相应的类型的(正或负)无穷大。如果正被映射到的值太小,精确表示(其指数比由标准值集合允许更小),则舍入到最近可表示的非规格化值或相同的符号的零的。

值集转换保留无穷大和NaN和不能改变被转换的值的符号。值集转换对是一个浮点型的不是一个值没有影响。

2.9。 特殊的方法

在Java虚拟机的水平,用Java编程语言(JLS§8.8)每一个构造显示为一个实例的初始化方法具有特殊名称<init>。这个名字是由编译器提供。由于名称<init>是不是有效的标识符,它不能直接用Java编程语言编写的程序中使用。实例的初始化方法可能只在Java虚拟机被调用invokespecial指令(invokespecial),并且它们可以只对未初始化的类实例调用。实例初始化方法取从衍生它的构造方法的访问权限(JLS 6.6节)。

类或接口具有至多一个类或接口初始化方法和被初始化(§5.5通过调用该方法)。类或接口的初始化方法具有特殊的名字<clinit>,不带任何参数,并且是无效的(§4.3.3)。

命名其他方法<clinit>class文件中都没有结果。他们不是类或接口初始化方法。它们不能被任何Java虚拟机指令被调用,并且永远不会被Java虚拟机本身的调用。

在一个class其版本号为51.0或以上的文件中,该方法另外还必须有其ACC_STATIC标志(§4.6以便为类或接口初始化方法设定)。

这个要求是在Java SE 7中引入的一个类文件,其版本号为50.0或以下,一个名为方法<clinit>是无效和不带任何参数被认为是类或接口初始化方法,无论其设置的ACC_STATIC标志。

这个名字<clinit>是由编译器提供。由于名称<clinit>是不是有效的标识符,它不能直接用Java编程语言编写的程序中使用。类和接口的初始化方法是由Java虚拟机里隐含调用; 他们是决不会从任何Java虚拟机指令调用,但只是间接调用的类的初始化过程的一部分。

一种方法是签名多态性如果以下所有条件都为真:

  • 它在声明java.lang.invoke.MethodHandle类。

  • 它类型的单一形式参数Object []

  • 它的返回类型Object

  • 它具有ACC_VARARGSACC_NATIVE标志设置。

在Java SE 8,唯一的签名多态性方法是invokeinvokeExact类的方法java.lang.invoke.MethodHandle

Java虚拟机提供了特殊处理,以在签名多态性方法invokevirtual指令(invokevirtual),以实现一个的调用方法处理。一种方法,手柄是强类型,直接可执行的参考到底层方法,构造,场,或类似的低层次操作(§5.4.3.5)中,用的参数可选变换或返回值。这些变换是相当普遍的,并且包括诸如图案作为转换,插入,缺失和取代。见java.lang.invoke在Java SE平台API包以获取更多信息。

2.10。 例外

在Java虚拟机的异常是由一个类的实例表示Throwable或它的一个子类。在从那里异常被抛出点控制的直接外地转移抛出一个异常的结果。

大多数异常同步发生,通过在它们发生的线程的行为的结果。异步异常,与此相反,可以潜在地发生在一个程序的执行的任何点。Java虚拟机抛出的三个原因之一例外:

  • 一个athrow指令(§ athrow)被执行。

  • 不正常的执行条件是同步Java虚拟机检测。这些异常不在程序中的任意点抛出,但在指令执行后,只有同步,要么:

    • 指定异常可能的结果,如:

      • 当指令体现违反Java编程语言的语义,例如以阵列的界限之外索引的操作。

      • 当在装载或程序的连接部分发生错误。

    • 导致对资源的一些限制时使用过多的内存被超过,例如。

  • 异步异常的发生是因为:

    • 所述stop类的方法ThreadThreadGroup已被调用,或

    • 在Java虚拟机实现中发生内部错误。

    stop方法可以由一个线程被调用以影响另一线程或指定的线程组中的所有线程。他们是异步的,因为他们可以在其他线程或线程的执行的任何地方出现。内部错误被认为是异步(§6.3)。

Java虚拟机可以允许执行的小,但数量有限,抛出异步异常之前发生。这种延迟允许允许进行优化的代码来检测和分它是可行的处理它们,同时遵守Java编程语言的语义抛出这些异常。

一个简单的实现可能轮询在每个控制转移指令的点异步异常。由于一个程序具有有限的尺寸,这提供了关于总延迟一个绑定中检测异步异常。由于没有异步异常将控制转移之间发生,代码发生器具有一定的灵活性以重新排序控制传输之间的计算获得更好的性能。本文有效地轮询对股票硬件马克·菲利,PROC。在函数式编程和计算机体系结构1993年会议,哥本哈根,丹麦,第179-187,建议进一步阅读。

由Java虚拟机抛出的异常被精确:当控制权转移发生,指令所有特效从中抛出异常必须出现有发生点之前执行。从中抛出异常点后出现没有说明可能出现了评价。如果优化的代码已推测执行的一些随后在其中发生异常的点处的指令,这样的代码必须准备隐藏从该程序的用户可见的状态这个推测执行。

在Java虚拟机的每个方法可与零个或多个相关联的异常处理程序。异常处理程序指定的偏移到实现用于该异常处理程序是活性的方法中的Java虚拟机代码的范围内,描述异常类型,该异常处理程序能够处理,并指定为处理代码的位置该异常。一个异常的异常处理程序相匹配导致异常是在异常处理程序的偏移量的范围和异常类型是相同的类或类异常的异常处理程序处理的一个子类的指令如果偏移。当一个异常被抛出,Java虚拟机搜索当前方法匹配的异常处理程序。如果匹配的异常处理程序被发现,该系统转移到由匹配的处理程序中规定的异常处理代码。

如果在当前方法没有找到这样的异常处理程序,当前方法调用突然结束(§2.6.5)。上突然结束后,将操作数堆栈和当前方法调用的本地变量被丢弃,且其帧被弹出,复原调用方法的框架。该异常然后在调用者的帧的上下文重新抛出等,持续了方法调用链。如果达到了方法调用链的顶部之前没有发现适合的异常处理程序,其中,所述异常发生的线程的执行结束。

其中一个方法的异常处理程序中查找匹配的顺序很重要。在一个class文件中,每个方法的异常处理程序被存储在表(§4.7.3)。在运行时,当一个异常被抛出,Java虚拟机将搜索当前方法的异常处理程序的顺序,它们出现在相应的异常处理程序表中class的文件,从该表的开头开始。

需要注意的是Java虚拟机不会强制嵌套或方法的异常表项的任何顺序。Java编程语言的异常处理语义仅通过与编译器(合作实施§3.12)。当class文件被通过其他方式产生的,所定义的搜索程序,确保所有的Java虚拟机实现的行为一致。

2.11。 指令集摘要

Java虚拟机指令由一字节的操作码指定要执行的操作,后面的零点或更多的操作数供给由操作使用的参数或数据。许多指令没有操作数,仅由一个操作码的。

忽略异常,Java虚拟机解释器的内循环是有效的

做{ 原子计算PC,并获取在PC操作码; 如果(操作数)取操作数; 执行用于操作码的操作; }而(有更多的事情要做); 

的操作数的数目和大小由操作码来确定。如果操作数的大小超过一个字节,则存储在大端顺序-高位字节。例如,一个16位无符号索引到局部变量被存储为两个无符号字节,字节1字节2,使得其值是(字节1 << 8)| 字节2

字节码指令流是唯一的单字节对齐。两个例外是lookupswitchtableswitch指令(§ lookupswitch§ tableswitch),其被填充以迫使上4字节边界的一些操作数的内部对准。

Java虚拟机操作码限制为一个字节,并放弃编译的代码中的数据排列的决定反映了有利于紧凑的有意识的偏见,可能在幼稚实现的一些性能为代价的。一个一字节操作码也限制了指令集的大小。不假设数据对齐意味着大于一个字节立即数据必须从字节在运行时被构造在许多机器上。

2.11.1。类型和Java虚拟机

大部分的Java虚拟机指令,指令集对他们执行的操作编码类型的信息。例如,ILOAD指令(§ ILOAD)加载的局部变量,它必须是一个的内容int,到操作数栈。该FLOAD指令(FLOAD)确实有相同的float价值。的两条指令可以具有相同的实现方式中,但具有不同的操作码。

对于大多数类型的指令,该指令类型在由字母操作码助记符显式地表示:用于int操作,longs ^shortbbyteÇchar˚Ffloatddouble,和一个reference。有些指令有类型是明确没有在他们的记忆类型的信。例如,arraylength始终工作的对象,则在阵列上。一些指令,如跳转 ,无条件控制转移,不要在输入的操作数。

由于Java虚拟机的一个字节的操作码大小,编码类型为操作码放在其指令集的设计压力。如果每个输入指令支持的所有Java虚拟机的运行时间数据类型的,将有超过一个字节可以表示更多的指令。相反,指令集Java虚拟机提供的某些操作类型的支持水平降低。换言之,该指令集是故意不正交。单独指令可以被用来在必要时不支持和支持的数据类型之间进行转换。

表2.11.1-A总结了指令集Java虚拟机的类型支持。的特定指令,与类型信息,通过替换内置Ť通过在类型列字母在指令模板在操作码列中。如果由于某种指令模板和类型类型栏为空白,则没有指令存在支持该类型的操作。例如,有一个类型的负载指令intILOAD,但对于类型没有加载指令byte

请注意,在大多数指令表2.11.1-A不具备整型形式bytecharshort。无有形式boolean类型。编译器编码类型的文字值的负载byteshort使用Java虚拟机指令符号扩展的值类型的值int在编译时或运行时。的类型的文字值的载荷booleanchar正在使用的字面零扩展到类型的值的指令编码int在编译时或运行时。同样地,从类型的值的阵列的负载booleanbyteshort,和char正在使用的Java虚拟机指令,符号扩展或零值,延伸到类型的值编码的int。因此,实际的类型的值的大多数操作booleanbytechar,和short正确地通过在计算类型的值操作说明进行int

表2.11.1-A。类型支持Java虚拟机指令集

操作码 byte short int long float double char reference
Tipush bipush sipush
TCONST ICONST lconst FCONST DCONST aconst
t加载 ILOAD 的Iload FLOAD DLOAD 负载
Tstore istore lstore FSTORE dstore 一家商店
TINC iinc
Taload baload saload iaload laload faload daload caload aaload
Tastore bastore sastore iastore lastore fastore dastore castore aastore
塔德 我加 拉德 FADD DADD
TSUB ISUB LSUB 一个fsub DSUB
Tmul IMUL lmul FMUL DMUL
Tdiv IDIV LDIV FDIV DDIV
兰珍珍 IREM lrem FREM 德雷姆
TNEG INEG lneg FNEG dneg
Tshl ISHL lshl
TSHR ISHR LSHR
Tushr iushr lushr
T和 我和 土地
托尔 IOR LOR
Txor ixor lxor
I2T I2B I2S I2L I2F I2D
L2T l2i L2F L2D
F2T F2I F2L F2D
D2T D2I D2L D2F
TCMP LCMP
Tcmpl fcmpl dcmpl
Tcmpg fcmpg dcmpg
if_TcmpOP if_icmpOP if_acmpOP
Treturn 我回来 lreturn freturn dreturn 回报

Java虚拟机的实际类型和Java虚拟机的计算类型之间的映射是通过汇总表2.11.1-B

某些Java虚拟机指令,如流行交换,而不考虑输入操作数堆栈上运行; 然而,这样的指令被限制为仅在某些类别的计算类型,在给出的值使用表2.11.1-B

表2.11.1-B。实际与计算类型的Java虚拟机

实际的类型 计算类型 类别
boolean int 1
byte int 1
char int 1
short int 1
int int 1
float float 1
reference reference 1
returnAddress returnAddress 1
long long 2
double double 2

2.11.2。加载和存储指令

加载和存储指令的局部变量(之间传送值§2.6.1)和操作数堆栈(§2.6.2 Java虚拟机帧(的)§2.6):

  • 加载本地变量压入操作数堆栈:ILOADiload_ <N> 的Iloadlload_ <N> FLOADfload_ <N> 进入dloaddload_ <N> aloadaload_ <N> 

  • 存储从操作数堆栈的值到一个局部变量:istoreistore_ <N> lstorelstore_ <N> FSTOREfstore_ <N> dstoredstore_ <N> ASTOREastore_ <N> 

  • 加载一个恒定到操作数栈:bipushsipushLDCldc_wldc2_waconst_nulliconst_m1iconst_ <I> lconst_ <L> fconst_ <F> dconst_ <d> 

  • 访问使用更宽的索引更局部变量,或为更大的立即操作数:

访问对象的字段和阵列元件中的指令(§2.11.5)也传送数据到和来自操作数堆栈。

与后角撑架之间的信上面所示的指令助记符(例如,iload_ <N> )表示的指令家族(其成员iload_0iload_1iload_2,和iload_3在的情况下iload_ <N> )。的指令,例如家庭是一个额外的通用指令(的特ILOAD,它有一个操作数)。对于特定的指令,操作数是隐含的,并不需要存储或取出。语义是否则相同的(iload_0意味着同样的事情ILOAD与操作数0)。尖括号之间的信指定隐含操作数的类型为家庭的指令:对于<N> ,一个非负整数; 为<I> ,一个int; 为<L> ,一个long; 为<F> ,一个float; 和用于<d> ,一个double。对于类型的形式int在很多情况下,用于在类型的值执行操作bytecharshort§2.11.1)。

这个符号对于指令的家庭在整个说明书中使用。

2.11.3。算术指令

算术指令计算结果,其通常操作数堆栈上的两个值的函数,推结果返回操作数堆栈上。主要有两种类型的算术指令:这些整数值运行,而那些对浮点值运行。在每一个这类的运算指令是专门为Java虚拟机的数字类型。存在用于整数算术上的值不直接支持byteshortchar类型(§2.11.1),或用于值boolean类型;这些操作是由上式操作说明进行处理int。整数和浮点指令也在他们的行为有所不同溢出和除以零。算术指令如下:

  • 地址:IADD拉德FADDDADD

  • 减:ISUBLSUB一个fsubDSUB

  • 乘法:IMULlmulFMULDMUL

  • 分歧:IDIVLDIVFDIVDDIV

  • 余:IREMlremFREM德雷姆

  • 否定:INEGlnegFNEGdneg

  • 转变:ISHLISHRiushrlshlLSHRlushr

  • 按位OR:IORLOR

  • 按位与:IAND土地

  • 按位异或:ixorlxor

  • 局部变量递增:iinc

  • 比较:dcmpgdcmplfcmpgfcmplLCMP

Java编程语言运营商的整数和浮点值(JLS§4.2.2,JLS§4.2.4)的语义直接由Java虚拟机指令集的语义支持。

Java虚拟机并不表示整数数据类型的操作过程中溢出。可以抛出异常的唯一整数操作是整数除法指令(IDIVLDIV)和整数余数的指令(IREMlrem),其抛出ArithmeticException如果除数为零。

在浮点数的Java虚拟机操作的行为在IEEE 754特别指定的,Java虚拟机需要全面支持IEEE 754的非规范化的浮点数和渐进下溢,这使得它更容易证明的特定数值算法所需的特性。

Java虚拟机需要浮点运算表现得好像每一个浮点操作的四舍五入浮点结果到结果的精度。不精确的结果必须被舍入到表示值最接近于无限精确的结果;如果两个最接近的可表示值同等的附近,选择具有零至少显著位之一。这是IEEE 754标准的默认舍入模式,被称为向最接近模式。

Java虚拟机使用IEEE 754 向零舍入浮点值转换为整数时的模式。这导致在被截断的数目;表示操作数值的小数部分的有效数字的任何位被丢弃。向零模式回合选择作为其结果最接近类型的值,但幅度比,无限精确的结果没有更大。

Java虚拟机的浮点运算符不抛出运行时异常(不符合IEEE 754浮点异常混淆)。该溢出的操作产生一个签名的无穷大,即下溢的操作产生非正规化值或符号零,并且没有明确的数学结果产生的NaN出操作。与南作为一个操作数的所有数字运算产生NaN的结果。

上类型的值的比较longLCMP)执行签名比较。上浮点类型(的值进行比较dcmpgdcmplfcmpgfcmpl使用IEEE 754非信令比较被执行)。

2.11.4。类型转换指令

类型转换指令允许Java虚拟机的数字类型之间的转换。这些可以被用来实现在用户代码显式转换或减轻在指令集Java虚拟机的缺乏正交性。

Java虚拟机直接支持下扩大数字转换:

  • intlongfloatdouble

  • longfloatdouble

  • float 至 double

日益扩大的数字转换指令I2LI2FI2DL2FL2DF2D。这些操作码的助记符是直接给出了类型化的说明和双关语使用的2是指的命名约定“到了。”例如,I2D指令转换的int一个值double

大多数扩大数字转换不会失去对数值的总体规模的信息。事实上,从转换扩大intlongintdouble不任何信息丢失;数值正好保存。换算从加宽floatdouble属于FP-严格(§2.8.2)也保存的数值完全相同;只有这样的转换不属于FP-严格可能会失去约转换后的值的总大小的信息。

从转化intfloat,或从longfloat,或从longdouble,可能会失去精度,即,可能会丢失一些值的至少显著比特; 所得浮点值是一个正确的舍入版本的整数值,使用IEEE 754舍入到最近的模式。

尽管可能出现的精度损失,扩大数字转换不会导致Java虚拟机抛出一个运行时异常(不带IEEE 754浮点异常混淆)。

的A扩大数值转换intlong简单地登录延伸的二进制补码表示int的值来填充较宽格式。的加宽数值转换char为整型零延伸的表示char值来填充较宽格式。

需要注意的是扩大数字转换不会从整型存在bytecharshort打字int。正如指出的§2.11.1类型,值bytechar以及short在内部加宽输入int,使得这些转换隐式的。

Java虚拟机还可以直接支持以下缩小数字转换:

  • intbyteshortchar

  • long 至 int

  • floatintlong

  • doubleintlongfloat

在缩小数字转换指令I2BI2CI2Sl2iF2IF2LD2ID2LD2F。甲变窄数值转换可导致不同的符号,以不同的顺序大小,或两者的值; 它可能因此而丧失精度。

的A缩小数字转换intlong为整型Ť简单地丢弃所有,但Ñ最低阶位,其中Ñ是用于表示类型比特的数目Ť。这可能导致所得到的值不具有相同的符号作为输入值。

在一个浮点值的为整型变窄数字转换Ť,其中Ť要么intlong,浮点值被转换如下:

  • 如果浮点值是NaN,则转换的结果是一个intlong 0

  • 否则,如果浮点值不是无限大,浮点值舍入为一个整数值,V使用IEEE 754向零舍入模式。有两种情况:

    • 如果Ť是long和该整数值可表示为一long,那么结果是longV

    • 如果Ť是类型int和该整数值可表示为一个int,则结果是intV

  • 除此以外:

    • 任一值必须是太小(大的幅度或负无穷大的负值),并且结果是类型的表示的最小值intlong

    • 或值必须是太大(幅度大或正无穷大的正值),其结果是类型的最大表示值intlong

从A变窄数值转换doublefloat根据IEEE 754。结果使用IEEE 754舍入到最近模式被正确地舍入的行为。过小的值被表示为float被转换成正或负型的零float; 作为要表示太大的值,float被转换为正或负无穷大。甲doubleNaN被总是被转换为一float为NaN。

尽管可能出现溢出,下溢或精度损失的事实,数字类型之间的缩小转换不会导致Java虚拟机抛出一个运行时异常(不带IEEE 754浮点异常混淆)。

2.11.5。对象的创建和操纵

虽然这两个类实例和数组都是对象,Java虚拟机创建和使用指令的不同套操纵类实例和数组:

  • 创建一个新的类的实例:

  • 创建一个新的数组:newarrayanewarraymultianewarray

  • 类(的访问的字段static的字段,称为类变量)和类实例的字段(非static:字段,被称为实例变量)getstaticputstaticgetfield命令putfield

  • 加载的阵列组件到操作数栈:baloadcaloadsaloadialoadlaloadfaloaddaloadaaload

  • 存储从操作数堆栈的值作为数组成分:bastorecastoresastoreiastorelastorefastoredastoreaastore

  • 得到数组的长度:arraylength

  • 检查类实例或阵列的属性:的instanceofcheckcast

2.11.6。操作数栈管理指令

提供了用于操作数堆栈的直接操纵的若干操作的指令:流行POP2DUPDUP2dup_x1dup2_x1dup_x2dup2_x2互换

2.11.7。控制转移指令

该控制转移指令有条件或无条件导致Java虚拟机继续与一个比控制传输指令之后其他的指令执行。 他们是:

  • 条件分支:IFEQifneIFLTifleifgtifgeIFNULLifnonnullif_icmpeqif_icmpneif_icmpltif_icmpleif_icmpgt if_icmpgeif_acmpeqif_acmpne

  • 复合条件分支:tableswitchlookupswitch

  • 无条件转移:跳转goto_wJSRjsr_wRET

Java虚拟机具有明显的集上与数据比较有条件分支指令intreference类型。它也有不同的条件分支指令,其测试对于空引用,因此它不需要指定一个具体的值null§2.4)。

上的类型的数据之间的比较的条件分支booleanbytechar,和short使用执行int比较指令(§2.11.1)。上的类型的数据之间的比较的条件分支longfloat或者double使用用于比较的数据,并产生一个指令被启动int的比较(结果§2.11.3)。随后int比较指令测试这个结果和效果的条件分支。由于公司注重的int比较,Java虚拟机提供了类型的条件分支指令的丰富的补充int

所有的int条件控制转移指令执行符号比较。

2.11.8。方法调用和返回指令

以下五个指令调用方法:

  • invokevirtual调用对象的实例方法中,调度在(虚拟)对象的类型。这是在Java编程语言中的正常方法分派。

  • invokeinterface调用接口方法,搜索由特定运行时对象实现以找到适当的方法中的方法。

  • invokespecial调用要求特殊处理的实例方法,无论是一个实例初始化方法(§2.9),一个private方法或超类方法。

  • invokestatic调用一个类别(static在名为类)方法。

  • invokedynamic调用其是键合于该调用位置对象的目标的方法 invokedynamic指令。呼叫站点对象被绑定到的特定词汇出现 invokedynamic由Java虚拟机的指令的第一执行之前运行的引导方法的结果的指令。因此,在每次出现时 invokedynamic指令具有唯一的链接状态,不同于调用方法的其它指令。

该方法返回指令,其通过返回类型区分,是ireturn(用于返回类型的值booleanbytecharshort,或int),lreturnfreturndreturn,和areturn。此外,返回指令被用来从被宣布为无效方法,实例初始化方法和类或接口的初始化方法返回。

2.11.9。 抛出异常

一个例外是使用编程方式抛出athrow指令。异常也可以通过不同的Java虚拟机指令,如果他们检测到异常情况被抛出。

2.11.10。 同步

Java虚拟机支持的两种方法和方法中的指令序列由单个同步结构同步:该显示器

方法级同步是隐含执行,如方法调用和返回(的一部分§2.11.8)。甲synchronized方法是在运行时间常量池中的区分method_info结构(§4.6)由ACC_SYNCHRONIZED标志,这是由方法调用指令进行检查。当调用用于其的方法ACC_SYNCHRONIZED被设置,正在执行的线程进入监视器,调用方法本身,并离开显示器的方法调用是否正常或突然结束。在执行线程拥有该显示器的时候,没有其他线程可以进入。如果是一个例外的调用过程中引发的synchronized方法和synchronized方法不处理异常,该方法的监控异常被重新抛出了之前会自动退出synchronized方法。

指令序列的同步典型地用于将编码synchronized的Java编程语言的块。Java虚拟机提供的monitorentermonitorexit指令来支持这样的语言结构。正确执行synchronized块需要从编译器针对Java虚拟机(合作§3.14)。

结构的锁定是这种情况时,在方法调用期间,给定的显示器上每一个退出该显示器上一前一入口相匹配。由于没有保证提交给Java虚拟机的所有代码将执行结构性锁定,Java虚拟机的实现是允许的,但不要求强制执行两个以下两条规则保证结构的锁定。让牛逼是一个线程,中号是监视器。然后:

  1. 通过执行监视器条目的数量Ť中号的方法调用期间必须等于由执行监控退出的数目Ť中号的方法调用方法调用是否正常地或突然地完成期间。

  2. 在一个方法调用期间没有点可以通过执行监控退出的数目Ť中号,因为该方法的调用超过执行监视器条目的数量Ť中号,因为该方法调用。

注意,监视器的进入和退出Java虚拟机调用时自动执行的synchronized方法被认为是调用方法的调用期间发生。

2.12。 类库

Java虚拟机必须提供Java SE平台的类库实现足够的支持。一些在这些库的类不能没有Java虚拟机的合作来实现。

可能需要从Java虚拟机的特殊支持类包括那些支持:

  • 反射,如在包中的类java.lang.reflect和类Class

  • 加载和一类或接口的创建。最明显的例子是类ClassLoader

  • 链接和一个类或接口的初始化阶段。以上引用的示例类属于这一类为好。

  • 安全性,如在包中的类java.security和其他类等SecurityManager

  • 多线程,如类Thread

  • 弱引用,如在包中的类java.lang.ref

上面的列表是意在说明,而不是全面的。这些类的或提供的功能详尽的清单超出了本规范的范围。请参阅Java SE平台类库的规格细节。

2.13。 公共设计,私有实现

迄今为止,该规范已拟定了Java虚拟机的公众观点:class文件格式和指令集。这些组件的硬件,运行系统-和Java虚拟机实现的独立性至关重要。实现者可能更愿意认为他们是安全通信的各执行的Java SE平台,而不是作为一个蓝图准确地跟踪主机之间的节目片段的手段。

理解,公众的设计和私有实现之间的界限就在于它是非常重要的。 Java虚拟机实现必须能够读取class文件,必须严格执行其中的Java虚拟机代码的语义。这样做的一种方式是利用这个文件作为规范和逐字实现该规范。但是,这也是完全可行的,并期望实现者来修改或优化本说明书的约束内执行。只要class文件格式可以读取和其代码的语义保持,实现者可以实现任何方式对这些语义。什么是“引擎盖下”是实现者的业务,只要正确的外部接口,精心维护。

也有一些例外:调试器,分析器,以及刚刚在时间码生成器可以在每个需要访问那些通常认为是Java虚拟机的元素在适当情况下,甲骨文的作品与其他Java虚拟机“引擎盖下”。实现者与工具厂商开发的通用接口的Java虚拟机使用此类工具,并推动整个行业的接口。

实现者可以利用这种灵活性来定制的Java虚拟机实现了高性能,低内存占用,或便携性。情理之中的事情给定的实现依赖于实现的目标。实施方案的范围包括以下内容:

  • 在加载时或执行到设置另一个虚拟机的指令期间平移Java虚拟机代码。

  • 翻译在加载时或执行到本机指令集的主CPU的过程中的Java虚拟机代码(有时被称为刚刚在时间,或JIT,代码生成)。

精确定义的虚拟机和目标文件格式的存在,不必显著限制实现者的创造力。Java虚拟机被设计为支持许多不同的实现,提供新的和有趣的解决方案,同时保留实现之间的兼容性。

翻译Java虚拟机的结构的更多相关文章

  1. Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法 ...

  2. JVM基础系列第6讲:Java 虚拟机内存结构

    看到这里,我相信大家对于一个 Java 源文件是如何变成字节码文件,以及字节码文件的含义已经非常清楚了.那么接下来就是让 Java 虚拟机运行字节码文件,从而得出我们最终想要的结果了.在这个过程中,J ...

  3. 从一道面试题深入了解java虚拟机内存结构

    记得刚大学毕业时,为了应付面试,疯狂的在网上刷JAVA的面试题,很多都靠死记硬背.其中有道面试题,给我的印象非常之深刻,有个大厂的面试官,顺着这道题目,一直往下问,问到java虚拟机的知识,最后把我给 ...

  4. 深入理解Java虚拟机04--类结构文件

    一.程序存储格式 统一的程序存储格式:不同平台的虚拟机于所有平台都统一使用程序存储格式——字节码(ByteCode); Java 虚拟机不关心 Class 文件的来源,而只和“Class文件" ...

  5. 深入剖析Java虚拟机内存结构

    深入剖析Java虚拟机内存模型 JVM整体架构 JVM整体架构如下: 通过编写代码来分析整个内存区域 public class Math { public static final Integer C ...

  6. Java虚拟机(一)结构原理与运行时数据区域

    我们来学习Java虚拟机的结构原理与运行时数据区域. 1.Java虚拟机概述 Oracle官方定义的Java技术体系主要包括以下几个部分: Java程序设计语言 各种平台的Java虚拟机 Class文 ...

  7. Java虚拟机 - 结构原理与运行时数据区域

    http://liuwangshu.cn/java/jvm/1-runtime-data-area.html 前言 本来计划要写Android内存优化的,觉得有必要在此之前介绍一下Java虚拟机的相关 ...

  8. [java] 虚拟机(JVM)底层结构详解[转]

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在以前的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本语法到 ...

  9. 【深入浅出-JVM】(5):Java 虚拟机结构

    Java 虚拟机基本结构 Java 堆 新生代.老年代划分 栈帧 感谢您的耐心阅读,如果您发现文章中有一些没表述清楚的,或者是不对的地方,请给我留言,您的鼓励是作者写作最大的动力. 作 者 : @mo ...

随机推荐

  1. python获取系统开机时间

    import psutil import time time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(psutil.boot_time()))

  2. SpringBoot2中配置文件的调整,升级SpringBoot2时候注意的坑

    原来使用SpringBoot1.5最近写个demo后发现原来的配置文件不能用了. 最后上网查询了一下资料,springboot2.0和spring1.x还是存在不少问题的. 1.问题一:Java版本要 ...

  3. Python实现微信刷卡支付(条码支付)MicroPay

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7686765.html 一:资料阅读 场景介绍:https://pay.weixin.qq.com/wiki/d ...

  4. 在vim中安装及配置NERDTree插件

    使用Vundle插件安装,在.vimrc中加入以下代码: Plugin 'scrooloose/nerdtree' 打开vim,输入命令如下: :BundleInstall 等待安装完毕 配置NERD ...

  5. POJ 2003 Hire and Fire (多重链表 树结构 好题)

    Hire and Fire Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 2316   Accepted: 655 Desc ...

  6. 【Linux】文件权限

    Linux的每一个文件都跟多种类型相关联.在这些权限中,我们通常需要和三类权限打交道(用户.用户组以及其他实体). 1.文件权限查看ls –l Linux:/qinys # ls -l total 6 ...

  7. V-rep学习笔记:Reflexxes Motion Library 2

    VREP中的simRMLMoveToPosition函数可以将静态物体按照设定的运动规律移动到指定的目标位置/姿态.If your object is dynamically enabled, it ...

  8. urllib2特点--超时设置

    # -*- coding: cp936 -*- #python 27 #xiaodeng #urllib2特点--超时设置 import urllib2 def urlopen(): url='htt ...

  9. 视频转换ffmpeg

    使用yum在centos下安装ffmpeg   ffmpeg -i IMG_1893.MOV -ab 56 -ar 22050 -b 500 -r 15 -s 640x480 test.mp4   说 ...

  10. 【图像处理】openCV光流法追踪运动物体

    openCV光流法追踪运动物体 email:chentravelling@163.com 一.光流简单介绍 摘自:zouxy09 光流的概念是Gibson在1950年首先提出来的.它是空间运动物体在观 ...