Chapter 2. The Structure of the Java Virtual Machine

This document specifies an abstract machine. It does not describe any particular implementation of the Java Virtual Machine.

就是说文章讲的是java虚拟机的规范,而不是某个特定的java虚拟机。

To implement the Java Virtual Machine correctly, you need only be able to read the class file format and correctly perform the operations specified therein. Implementation details that are not part of the Java Virtual Machine's specification would unnecessarily constrain the creativity of implementors. For example, the memory layout of run-time data areas, the garbage-collection algorithm used, and any internal optimization of the Java Virtual Machine instructions (for example, translating them into machine code) are left to the discretion of the implementor.

如果想自己实现一个虚拟机,您只需要能够按照格式读取字节码文件并正确执行其中指定的操作 就行。具体实现细节不是这部分的内容,留给虚拟机开发者去完成。例如运行时数据区的内存布局,垃圾回收的算法,还有虚拟机内部指令的具体优化(如如何将指令转换为机器语言)都留给虚拟机的开发者去实现

2.1. The class File Format

Compiled code to be executed by the Java Virtual Machine is represented using a hardware- and operating system-independent binary format, typically (but not necessarily) stored in a file, known as the class file format. The class file format precisely defines the representation of a class or interface, including details such as byte ordering that might be taken for granted in a platform-specific object file format.

代码经过编译后,会以二进制的形式按照一定的规则和格式放到一个文件中(.class文件),这个文件是独立于硬件和操作系统的,然后java虚拟机执行的就是这个class文件。这个class文件中精确的定义了类和接口中的所有内容。【1】

【1】:里面存了比如类的权限定性,字段名字,字段的类型,方法名,返回值类型等所有信息

2.2. Data Types

Like the Java programming language, the Java Virtual Machine operates on two kinds of types: primitive types and reference types. There are, correspondingly, two kinds of values that can be stored in variables, passed as arguments, returned by methods, and operated upon: primitive values and reference values.

对于java语言来说,java虚拟机可以操作两种类型:基本数据类型和引用类型 。因此变量可以存储的,参数可以传递的,方法可以返回的值也只能是这两种类型的值。

The Java Virtual Machine expects that nearly all type checking is done prior to run time, typically by a compiler, and does not have to be done by the Java Virtual Machine itself. Values of primitive types(2.3) need not be tagged or otherwise be inspectable to determine their types at run time, or to be distinguished from values of reference types. Instead, the instruction set of the Java Virtual Machine distinguishes its operand types using instructions intended to operate on values of specific types. For instance, iaddladdfadd, and dadd are all Java Virtual Machine instructions that add two numeric values and produce numeric results, but each is specialized for its operand type: intlongfloat, and double, respectively. For a summary of type support in the Java Virtual Machine instruction set, see §2.11.1.

java虚拟机期望的是所有的类型检查能在程序运行之前由编译器完成,而不是交给java虚拟机来完成。基本数据类型的值可在运行时决定他们的类型,此段稍后翻译,暂时不明白

The Java Virtual Machine contains explicit support for objects. An object is either a dynamically allocated class instance or an array. A reference to an object is considered to have Java Virtual Machine type reference. Values of type reference can be thought of as pointers to objects. More than one reference to an object may exist. Objects are always operated on, passed, and tested via values of type reference.

java虚拟机支持对象这个东西,一个对象可以是动态分配的一个类的实例,也可以是一个数组。对某个对象的引用,这个引用必须是java虚拟机支持的类型(基本类型引用和引用类型)。类型引用的值可以认为是一个指向对象的指针。可以有好多个引用同时指向某个对象。通常都是通过这个类型引用的值(指针)来传递或者操作对象(方法中参数是对象时,传过来的参数都是引用,也就是把对象所在的地址传过来)

一个对象赋值给一个变量,这个变量前面会定义一个类型,这个类型就是引用类型,通常是类的名字。这个变量中存的就是引用的值,也就是我们说的里面放了一个指向堆中对象的指针。

2.3. Primitive Types and Values

The primitive data types supported by the Java Virtual Machine are the numeric types, the boolean type (§2.3.4), and the returnAddress type (§2.3.3).

java虚拟机支持的基本数据类型有numeric类型boolean类型returnAddress类型

The numeric types consist of the integral types (2.3.1) and the floating-point types (2.3.2).
numeric类型包含integral类型(整数类型)和floating-point类型(浮点类型)

The integral types are: 整数类型包含如下:

The floating-point types are:浮点类型有:

The values of the boolean type encode the truth values true and false, and the default value is false.

boolean类型的值有true和false,默认值为false

The First Edition of The Java® Virtual Machine Specification did not consider boolean to be a Java Virtual Machine type. However, boolean values do have limited support in the Java Virtual Machine. The Second Edition of The Java® Virtual Machine Specification clarified the issue by treating boolean as a type.

第一版的java虚拟机中没有考虑把boolean类型作为java虚拟机的 类型。第二版开始才正式作为一个类型。

The values of the returnAddress type are pointers to the opcodes of Java Virtual Machine instructions. Of the primitive types, only the returnAddress type is not directly associated with a Java programming language type.

returnAddress类型是一个指向虚拟机指令操作码的指针。在基本类型的三种类型中只有retrunAddress类型与java语言中的数据类型没有直接关系(要分清jvm中的数据类型,与java语言的数据类型不是一回事,之不过有关系而已)。

2.3.1. Integral Types and Values

The values of the integral types of the Java Virtual Machine are:

Java虚拟机中整数类型的值有以下几种:

  • byte,取值范围从-128到127
  • short,取值范围从-32768到32767
  • int,取值范围从 -2147483648 到 2147483647
  • long,取值范围从-9223372036854775808 到 9223372036854775807
  • char,取值范围从0到65535 (char占16位,也就是2的16次方=65536)

2.3.2. Floating-Point Types, Value Sets, and Values

The floating-point types are float and double, which are conceptually associated with the 32-bit single-precision and 64-bit double-precision format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std. 754-1985, New York).

The IEEE 754 standard includes not only positive and negative sign-magnitude numbers, but also positive and negative zeros, positive and negative infinities, and a special Not-a-Number value (hereafter abbreviated as "NaN"). The NaN value is used to represent the result of certain invalid operations such as dividing zero by zero.

Every implementation of the Java Virtual Machine is required to support two standard sets of floating-point values, called the float value set and the double value set. In addition, an implementation of the Java Virtual Machine may, at its option, support either or both of two extended-exponent floating-point value sets, called the float-extended-exponent value set and the double-extended-exponent value set. These extended-exponent value sets may, under certain circumstances, be used instead of the standard value sets to represent the values of type float or double.

The finite nonzero values of any floating-point value set can all be expressed in the form s ⋅ m ⋅ 2(e − N + 1), where s is +1 or −1, m is a positive integer less than 2N, and e is an integer between Emin = −(2K−1−2) and Emax = 2K−1−1, inclusive, and where N and K are parameters that depend on the value set. Some values can be represented in this form in more than one way; for example, supposing that a value v in a value set might be represented in this form using certain values for sm, and e, then if it happened that m were even and e were less than 2K-1, one could halve m and increase e by 1 to produce a second representation for the same value v. A representation in this form is called normalized if m ≥ 2N-1; otherwise the representation is said to be denormalized. If a value in a value set cannot be represented in such a way that m ≥ 2N-1, then the value is said to be a denormalized value, because it has no normalized representation.

The constraints on the parameters N and K (and on the derived parameters Emin and Emax) for the two required and two optional floating-point value sets are summarized in Table 2.3.2-A.

Table 2.3.2-A. Floating-point value set parameters

Parameter float float-extended-exponent double double-extended-exponent
N 24 24 53 53
K 8 ≥ 11 11 ≥ 15
Emax +127 ≥ +1023 +1023 ≥ +16383
Emin -126 ≤ -1022 -1022 ≤ -16382

Where one or both extended-exponent value sets are supported by an implementation, then for each supported extended-exponent value set there is a specific implementation-dependent constant K, whose value is constrained by Table 2.3.2-A; this value K in turn dictates the values for Emin and Emax.

Each of the four value sets includes not only the finite nonzero values that are ascribed to it above, but also the five values positive zero, negative zero, positive infinity, negative infinity, and NaN.

Note that the constraints in Table 2.3.2-A are designed so that every element of the float value set is necessarily also an element of the float-extended-exponent value set, the double value set, and the double-extended-exponent value set. Likewise, each element of the double value set is necessarily also an element of the double-extended-exponent value set. Each extended-exponent value set has a larger range of exponent values than the corresponding standard value set, but does not have more precision.

The elements of the float value set are exactly the values that can be represented using the single floating-point format defined in the IEEE 754 standard, except that there is only one NaN value (IEEE 754 specifies 224-2 distinct NaN values). The elements of the double value set are exactly the values that can be represented using the double floating-point format defined in the IEEE 754 standard, except that there is only one NaN value (IEEE 754 specifies 253-2 distinct NaN values). Note, however, that the elements of the float-extended-exponent and double-extended-exponent value sets defined here do not correspond to the values that can be represented using IEEE 754 single extended and double extended formats, respectively. This specification does not mandate a specific representation for the values of the floating-point value sets except where floating-point values must be represented in the class file format (§4.4.4, §4.4.5).

The float, float-extended-exponent, double, and double-extended-exponent value sets are not types. It is always correct for an implementation of the Java Virtual Machine to use an element of the float value set to represent a value of type float; however, it may be permissible in certain contexts for an implementation to use an element of the float-extended-exponent value set instead. Similarly, it is always correct for an implementation to use an element of the double value set to represent a value of type double; however, it may be permissible in certain contexts for an implementation to use an element of the double-extended-exponent value set instead.

Except for NaNs, values of the floating-point value sets are ordered. When arranged from smallest to largest, they are negative infinity, negative finite values, positive and negative zero, positive finite values, and positive infinity.

Floating-point positive zero and floating-point negative zero compare as equal, but there are other operations that can distinguish them; for example, dividing 1.0 by 0.0 produces positive infinity, but dividing 1.0 by -0.0 produces negative infinity.

NaNs are unordered, so numerical comparisons and tests for numerical equality have the value false if either or both of their operands are NaN. In particular, a test for numerical equality of a value against itself has the value false if and only if the value is NaN. A test for numerical inequality has the value true if either operand is NaN.

暂不翻译。。。。

2.3.3. The returnAddress Type and Values

The returnAddress type is used by the Java Virtual Machine's jsrret, and jsr_w instructions (§jsr, §ret, §jsr_w). The values of the returnAddress type are pointers to the opcodes of Java Virtual Machine instructions. Unlike the numeric primitive types, the returnAddress type does not correspond to any Java programming language type and cannot be modified by the running program.

java虚拟机的jsr,ret,jsr_w指令会用到returnAddress类型。returnAddress类型的值是一个指针,这个指针指向java虚拟机指令的操作码。不同于基本数据类型的numeric类型,returnAddress类型与任何Java编程语言类型都不对应,并且无法由正在运行的程序修改。【1】

【1】:returnAddress类型以及其值是java虚拟机生成的,他记录了比如方法栈中下一条要执行的指令的地址。所以我们无法通过代码在运行时修改它。一般都是放在pc寄存器中的

2.3.4. The boolean Type

Although the Java Virtual Machine defines a boolean type, it only provides very limited support for it. There are no Java Virtual Machine instructions solely dedicated to operations on boolean values. Instead, expressions in the Java programming language that operate on boolean values are compiled to use values of the Java Virtual Machine int data type.

尽管Java虚拟机定义了boolean类型,但它只提供了非常有限的支持。java虚拟机没有专门的指令对应boolean类型的值。而是将Java语言中对布尔值的操作都被编译器编译为了Java虚拟机的int数据类型的值。

The Java Virtual Machine does directly support boolean arrays. Its newarray instruction (§newarray) enables creation of boolean arrays. Arrays of type boolean are accessed and modified using the byte array instructions baload and bastore (§baload, §bastore).

Java虚拟机支持boolean数组。可以通过创建数组的指令创建一个boolean类型数组。访问和修改boolean类型数组使用的是byte数组指令baload和bastore来完成的。【1】

【1】:上面说过java虚拟机对boolean类型的支持不是很多。但是支持在java程序中创建boolean类型的数组,例如:boolean[] b = new boolean[3]。java虚拟中对于boolean数组的操作,实际使用的是操作字节用的操作指令baload和bastore。

In Oracle’s Java Virtual Machine implementation, boolean arrays in the Java programming language are encoded as Java Virtual Machine byte arrays, using 8 bits per boolean element.

在Orcale生产的java虚拟机中(比如hotspot),针对java语言中的booean数组会被 编码成一个字节数组,每个boolean值使用8位空间存储。

The Java Virtual Machine encodes boolean array components using 1 to represent true and 0 to represent false. Where Java programming language boolean values are mapped by compilers to values of Java Virtual Machine type int, the compilers must use the same encoding.

java虚拟机中用1来表示boolean类型的true,用0表示boolean类型的false 。当编译器将Java语言的boolean值映射到Java虚拟机类型int的值时,编译器必须使用相同的编码(true编译成1,false编译成0)。

2.4. Reference Types and Values

There are three kinds of reference types: class types, array types, and interface types. Their values are references to dynamically created class instances, arrays, or class instances or arrays that implement interfaces, respectively.

引用类型一共有三种:class类型,数组类型,接口类型。 它们的值是对动态创建的类实例、数组或实现接口的类实例或数组的引用【1】

【1】:所谓动态就是指jvm运行时创建在堆中的对象或者通过反射创建的对象。然后对象所在地址也就是所谓的引用,将这个引用赋值给3种引用类型

An array type consists of a component type with a single dimension (whose length is not given by the type). The component type of an array type may itself be an array type. If, starting from any array type, one considers its component type, and then (if that is also an array type) the component type of that type, and so on, eventually one must reach a component type that is not an array type; this is called the element type of the array type. The element type of an array type is necessarily either a primitive type, or a class type, or an interface type.

就是说数组类型中的元素的类型必须是基本数据类型,class类型或者接口类型。数组里面还可以方数组 。此段可以忽略。

reference value may also be the special null reference, a reference to no object, which will be denoted here by null. The null reference initially has no run-time type, but may be cast to any type. The default value of a reference type is null.

可以将一个null引用赋值给引用类型,表示这个引用没有指向任何对象。null引用初始化的时候没有对应的类型,而且null引用可以转换为任何引用。一个引用类型的默认是就是null【1】

【1】:这段的意思就是java代码中可以给对象赋值为null。

2.5. Run-Time Data Areas(运行时数据区)

The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.

Java虚拟机定义了程序执行期间使用的各种运行时数据区域。这些数据区中的一部分在java虚拟机启动的时候被创建,java虚拟机关闭的时候被销毁【1】。还有一部分数据区称为Per-thread数据区,他们在创建线程时被创建,线程销毁时销毁【2】。

【1】:共享区域
【2】:专门给每个线程自己使用的数据区域
运行时数据区是jvm中很重要的一部分,具体各个区域介绍参考jvm实现篇中的讲解

运行时数据区中主要包括下面这些区域

2.5.1. The pc Register

The Java Virtual Machine can support many threads of execution at once (JLS §17). Each Java Virtual Machine thread has its own pc (program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method (§2.6) for that thread. If that method is not native, the pc register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread is native, the value of the Java Virtual Machine's pc register is undefined. The Java Virtual Machine's pc register is wide enough to hold a returnAddress or a native pointer on the specific platform.

java虚拟机支持同时有多个线程执行。每个线程都单独有一个自己的pc寄存器。当某个线程中的方法执行的时候,这个方法被称为当前方法。如果这个方法不是本地方法【1】,pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果这个方法是本地方法,那么pc寄存器就是undefined。java虚拟机的pc寄存器保存 returnAddress 或者 a native pointer是完全没有问题的【2】

【1】:本地方法指的是用c或者c++写的dll库等非java中自己写的代码。一般java方法上含有native的方法成为native方法,他们直接调用的是c的库
【2】:从这句可以看出上面在returnAddress中提到的,returnAddress一般作用于pc寄存器中
这段主要就是讲当调用线程中的方法时,如果调用的是本地方法,pc寄存器的值就是undefined,如果调用的是java写的方法,那么pc寄存器保存的就是当前执行的指令所在的地址。

2.5.2. Java Virtual Machine Stacks

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous.

java虚拟中的每个线程各自都会有一个私有的java虚拟机栈,这个java虚拟机栈在创建线程的时候创建。java虚拟机栈中存储着frames(栈帧§2.6)。Java虚拟机堆栈类似于传统语言(如C)的堆栈:它里面存储着局部变量(方法中的变量),部分结果,还有调用方法的返回地址【1】。java虚拟机栈处理只负责将frames(栈帧)压入到自己里面或者从自己这弹出外,不进行任何其他操作,frames(栈帧)也可以分配到堆中【2】。java虚拟机栈中的内存不需要是连续的。

【1】:这个请参考关于jvm实现中的虚拟机栈部分的讲解
【2】:说java虚拟机栈可以分配到堆里,这句话的意思是java虚拟机栈中的栈帧放到哪堆里还是虚拟机栈中由java虚拟机的实现者来决定。但是我们jdk1.8hotspot的frames(栈帧)是放在虚拟机栈中的

In the First Edition of The Java® Virtual Machine Specification, the Java Virtual Machine stack was known as the Java stack.

第一版的java虚拟机规范中,java虚拟机栈 也被称为java栈

This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created.

java虚拟机规范允许Java虚拟机堆栈可以是固定大小的,也可以根据计算动态扩展和收缩其容量 。如果想固定Java虚拟机堆栈的大小,则可以在创建该堆栈时单独设置每个Java虚拟机堆栈的大小。【1】

【1】:设置虚拟机栈大小的方法:-Xss size

Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes.

java虚拟机可以让使用者控制Java虚拟机栈初始化时的大小,以及在动态扩容或者缩容时最大和做小的容量

The following exceptional conditions are associated with Java Virtual Machine stacks:

以下的异常都与java虚拟机栈 有关:

2.5.3. Heap

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated.

java虚拟机又一个被所有线程共享的区域成为heap(堆)。 堆是一个运行时数据区,所有的类的实例和数组都会分配到堆中。

The heap is created on virtual machine start-up. Heap storage for objects is reclaimed by an automatic storage management system (known as a garbage collector); objects are never explicitly deallocated. The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor's system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous.

堆是在java虚拟机启动的时候创建的。堆中存储的对象会被垃圾回收器自动回收。对象本身是不会自己删除的。java虚拟机规范没有规定具体使用什么样的垃圾回收器,这个由java虚拟机自己决定。堆的大小同样既可以设置为固定大小,也可以动态的扩容或者缩容。堆中的内存空间不需要是连续的

A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the heap, as well as, if the heap can be dynamically expanded or contracted, control over the maximum and minimum heap size.

java虚拟机可以让使用者控制堆初始化时的大小,以及在动态扩容或者缩容时最大和做小的容量

The following exceptional condition is associated with the heap:

  • If a computation requires more heap than can be made available by the automatic storage management system, the Java Virtual Machine throws an OutOfMemoryError.

下面的异常跟堆有关系:

  • 如果需要的内存比可用的内存空间大,java虚拟机会报 OutOfMemoryError异常

2.5.4. Method Area

The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.

方法区是java虚拟机中所有线程共享的一个区域。这句话可忽略没有意义。方法区中存储着每个类的结构信息比如运行时常量池,类中的字段,类中的方法,还有方法中的代码和构造器【1】

【1】:方法区存储的其实就是class文件中的内容。class文件中含有类的所有信息。如果不明白class文件中有什么,可以先学习一下。

The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.

方法区在java虚拟机 启动时创建。虽然方法区在逻辑上属于堆的一部分,但是如果jvm相对比较简单则可以不实现垃圾回收或者压缩【1】。本规范不强制要求方法区域的位置或用于管理已编译代码的策略【2】。方法区域可以是固定大小,也可以根据计算要求进行扩展,如果不需要更大的方法区域,则可以收缩。方法区中的内存空间不需要是连续的

【1】:方法区是一个逻辑概念,在jdk8中方法区的落地实现叫做元空间,jdk1.7叫做永久代。
jdk1.8时方法区就不堆里了,这里要注意一下。
【2】:正因为规范不要方法区的位置,所以jdk1.8没有放到堆里。
这个具体参考jvm实现中对方法区的介绍

A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.

java虚拟机可以让使用者控制方法区初始化时的大小,以及在动态扩容或者缩容时最大和做小的容量。

jdk1.8: -XX:MetaspaceSize-XX:MaxMetaspaceSize设置元空间初始大小以及最大可分配大小
jdk1.7(以前)-XX:PermSize设置永久代初始大小。-XX:MaxPermSize设置永久代最大可分配空间

The following exceptional condition is associated with the method area:

  • If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.

下面的异常跟方法区有关系:

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

2.5.5. Run-Time Constant Pool

run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file (§4.4). It contains several kinds of constants, ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time. The run-time constant pool serves a function similar to that of a symbol table for a conventional programming language, although it contains a wider range of data than a typical symbol table.

运行时常量池就是class文件中constant_pool table的表现形式【1】。运行是常量池中包含了一些在编译时期就会确定下来的字符串常量和一些符号引用,这些符号引用在运行时会解析为字段引用和方法引用【2】。运行时常量池的功能类似于传统编程语言的符号表,尽管它包含比典型符号表更广泛的数据范围

【1】:Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种常量(字符串,static和final声明的变量的值)和符号引用,这部分内容将在类加载后存放到运行时常量池中。
【2】:这句简单来说就是常量池中一般存放两种东西:字面量(Literal)和符号引用量(Symbolic References)。numeric literals就是指符号引用。符号引用在类加载的linking阶段会解析为地址引用。具体可参考字节码常量池部分

Each run-time constant pool is allocated from the Java Virtual Machine's method area (§2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created (§5.3) by the Java Virtual Machine.

每个运行时常量池会被分配到java虚拟机的方法区。类或者接口的运行时常量池会在java虚拟机创建类或者接口的时候创建。

The following exceptional condition is associated with the construction of the run-time constant pool for a class or interface:

  • When creating a class or interface, if the construction of the run-time constant pool requires more memory than can be made available in the method area of the Java Virtual Machine, the Java Virtual Machine throws an OutOfMemoryError.

See §5 (Loading, Linking, and Initializing) for information about the construction of the run-time constant pool.

下面的异常跟运行时常量池的创建有关系:

  • 当创建类和或者接口时,如果方法区中没够足够的内存来创建对应的运行时常量池,java虚拟机会报OutOfMemoryError异常

Loading,Linking和Initializing章节会介绍更多跟创建运行时常量池相关的信息

2.5.6. Native Method Stacks

An implementation of the Java Virtual Machine may use conventional stacks, colloquially called "C stacks," to support native methods (methods written in a language other than the Java programming language). Native method stacks may also be used by the implementation of an interpreter for the Java Virtual Machine's instruction set in a language such as C. Java Virtual Machine implementations that cannot load native methods and that do not themselves rely on conventional stacks need not supply native method stacks. If supplied, native method stacks are typically allocated per thread when each thread is created.

java虚拟机也可以使用常规的堆栈(也可称为C堆栈)来支持本地方法的调用(所谓本地方法就是使用其他语言编写的方法,比如C语言。),这个常规的堆栈就称为本地方法栈。本机方法堆栈也可被Java虚拟机指令集的解释器(如C语言)使用【1】。无法加载本机方法且自身不依赖于传统堆栈的Java虚拟机实现不需要提供本机方法堆栈【2】。如果提供,则在创建每个线程时,通常会为每个线程分配本机方法堆栈。

【1】:如果你自己实现一个虚拟机,那么你的虚拟机用来解释指令的解释器就可以使用本地方法栈类存储东西。
【2】:就是说你自己实现的虚拟机完全可以不提供本地方方法栈

This specification permits native method stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the native method stacks are of a fixed size, the size of each native method stack may be chosen independently when that stack is created.

Java虚拟机规范运行本地方法栈既可以是固定大小的也可以根据需求动态的扩容或者缩容。如果想固定本地方法栈的大小,可以独立设置每个本机方法栈的大小。

A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the native method stacks, as well as, in the case of varying-size native method stacks, control over the maximum and minimum method stack sizes.

java虚拟机可以让使用者控制本地方法栈初始化时的大小,以及在动态扩容或者缩容时最大和做小的容量。

The following exceptional conditions are associated with native method stacks:

下面的异常跟本地方法栈有关系:

2.6. Frames

frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.

栈帧(放在java虚拟机栈当中,所以叫栈帧,也可以叫帧)用于存储数据和部分结果,以及执行动态链接,方法的返回值和异常。

A new frame is created each time a method is invoked. A frame is destroyed when its method invocation completes, whether that completion is normal or abrupt (it throws an uncaught exception). Frames are allocated from the Java Virtual Machine stack (§2.5.2) of the thread creating the frame. Each frame has its own array of local variables (§2.6.1), its own operand stack (§2.6.2), and a reference to the run-time constant pool (§2.5.5) of the class of the current method.

每当调用一个方法时便会创建一个栈帧。当方法调用完后或者方法抛出异常时栈帧一同销毁。栈帧分配在每个线程的Java虚拟机栈中。每个栈帧拥有自己单独的local variables(局部变量),operand stack(操作数栈)以及当前方法所在类的运行时常量池的引用。

A frame may be extended with additional implementation-specific information, such as debugging information.

你也可以使用附加的信息来扩展栈帧中的内容。这段可以忽略。

The sizes of the local variable array and the operand stack are determined at compile-time and are supplied along with the code for the method associated with the frame (§4.7.3). Thus the size of the frame data structure depends only on the implementation of the Java Virtual Machine, and the memory for these structures can be allocated simultaneously on method invocation.

local variable array和operand stack的大小在编译时就确定下来了。其内容也会被放在栈帧所对应的方法中的code属性中一并被提供【1】。因此,栈帧数据结构的大小仅取决于Java虚拟机的实现,并且这些结构的内存可以在方法调用时同时分配。

【1】:这个code不是指java代码,再介绍字节码部分会介绍这个code是个什么。看下图你就知道code是什么了,里面存着local variable等内容,具体在字节码部分详细介绍

Only one frame, the frame for the executing method, is active at any point in a given thread of control. This frame is referred to as the current frame, and its method is known as the current method. The class in which the current method is defined is the current class. Operations on local variables and the operand stack are typically with reference to the current frame.

在任何一个时刻只有一个栈帧能处于活动状态,哪个线程的方法在执行,那么这个方法所对应的栈帧就是活动状态。处于活动状态的帧称为当前帧,其对应的方法称为当前方法。当前方法对应的类叫做当前类。对local variables(局部变量)和operand stack(操作数栈)的操作通常也是针对当前帧中的局部变量和操作数栈进行的。

A frame ceases to be current if its method invokes another method or if its method completes. When a method is invoked, a new frame is created and becomes current when control transfers to the new method. On method return, the current frame passes back the result of its method invocation, if any, to the previous frame. The current frame is then discarded as the previous frame becomes the current one.

如果当前栈帧所对应的方法调用另外一个方法时或者方法执行结束后,那么这个栈帧将不再是当前栈帧。当一个方法被调用时,会创建一个新的栈帧,并随着控制权转交到当前方法时变为当前帧【1】。当方法调用完成后,这个栈帧将返回这个方法的结果给之前的那个栈帧。当前的栈帧就会被废弃,之前调用这个方法的那个栈帧将成为当前栈帧。

【1】:意思就是方法被调用时就会创建一个新的栈帧,然后这个栈帧就会成为当前栈帧

Note that a frame created by a thread is local to that thread and cannot be referenced by any other thread.

注意,栈帧只属于创建他的那个线程,其他线程是无法使用的。

2.6.1. Local Variables

Each frame (§2.6) contains an array of variables known as its local variables. The length of the local variable array of a frame is determined at compile-time and supplied in the binary representation of a class or interface along with the code for the method associated with the frame (§4.7.3).

每个栈帧中都包含着一个存放方法中变量的数组称为local variables(局部变量表) 。它的大小在编译时就会被确定下来,然后存放class字节码文件中的某个方法对应的code属性中【1】

【1】:通过javac编译java文件时,就会知道local variables的大小,然后会把这个值保存在class文件中的某个位置。你现在再是可以理解为放在class文件中一个叫做code的属性中。如下图(jclassLib),具体的在将字节码部分会详细介绍

A single local variable can hold a value of  type booleanbytecharshortintfloatreference, or returnAddress. A pair of local variables can hold a value of type long or double.

local variable中可以保存booleanbytecharshortintfloatreference, or returnAddress类型的值。一对local variables可以保存long和double类型的值【1】

【1】:local variables中有个叫做槽的概念,每个槽占32位空间,如果是long和double是64位的,所以需要2个槽才能放下。这也就是为什么说是a pair(一对)的意思。从数组的角度来说就是需要用2个索引空间来表示一个值。这个在jvm具体实现的文章中我会详细讲解。这里就理解为需要2块内存空间来保存lang和double的值就行。

Local variables are addressed by indexing. The index of the first local variable is zero. An integer is considered to be an index into the local variable array if and only if that integer is between zero and one less than the size of the local variable array.

Local variables通过索引来操作。local varibales的第一个索引值为0。当且仅当 local variable array 中元素的个数小于整数的最大值时,才会使用整数作为索引。

A value of type long or type double occupies two consecutive local variables. Such a value may only be addressed using the lesser index. For example, a value of type double stored in the local variable array at index n actually occupies the local variables with indices n and n+1; however, the local variable at index n+1 cannot be loaded from. It can be stored into. However, doing so invalidates the contents of local variable n.

这段不每句都翻译了。直接说一下整段讲的意思:就是说long和double类型的数据在local variables中要占用2个连续的位置,因为前面说过long和double占64位空间,local variables中每个索引能存放的大小是32位。比如将一个double类型的值保存在local variables中索引为n的位置,实际上它会占用n和n+1两个位置。此时如果你在n+1的位置上有存入了另外一个值,那么索引n位置中的值就变成无效的了,因为原先n和n+1共同组成double类型的值。

The Java Virtual Machine does not require n to be even. In intuitive terms, values of types long and double need not be 64-bit aligned in the local variables array. Implementors are free to decide the appropriate way to represent such values using the two local variables reserved for the value.

Java虚拟机不要求n为偶数。直观地说,long和double类型的值在局部变量数组中不需要64位对齐。所以实现者可以自由的决定一种合适的方式来使用这两个位置来保存long或者double类型的值。

The Java Virtual Machine uses local variables to pass parameters on method invocation. On class method invocation, any parameters are passed in consecutive local variables starting from local variable 0. On instance method invocation, local variable 0 is always used to pass a reference to the object on which the instance method is being invoked (this in the Java programming language). Any parameters are subsequently passed in consecutive local variables starting from local variable 1.

方法调用时的参数也存放在local variables中。当方法是类方法时, 参数会按照顺序从local variables中的索引0处开始存放。当方法是实例方法时,local variables的索引0的位置会固定存放一个引用,这个引用就是这个方法所在的对象,也就是this【1】。然后方法的参数则会从local variables索引1的位置开始按照顺序存放。

【1】:调用实例方法时,创建的frame栈帧中的local variables中索引0的位置保存的就是this,看下图(jclasslib插件):

总结:local variables局部变量表顾名思义就是存放方法中声明的变量的(同时包含方法的参数)

2.6.2. Operand Stacks

Each frame (§2.6) contains a last-in-first-out (LIFO) stack known as its operand stack. The maximum depth of the operand stack of a frame is determined at compile-time and is supplied along with the code for the method associated with the frame (§4.7.3).

每个栈帧中都包含一个后进先出的stack(堆栈)成为操作数栈。这个操作数栈的最大深度在编译时确定,保存在code属性中(上面讲过字节码中的code属性了,这里就不多做介绍了)。

Where it is clear by context, we will sometimes refer to the operand stack of the current frame as simply the operand stack.

当上下文比较清晰的时候,有时候直接把当前帧的操作数栈简称为操作数栈

The operand stack is empty when the frame that contains it is created. The Java Virtual Machine supplies instructions to load constants or values from local variables or fields onto the operand stack. Other Java Virtual Machine instructions take operands from the operand stack, operate on them, and push the result back onto the operand stack. The operand stack is also used to prepare parameters to be passed to methods and to receive method results.

栈帧刚创建的时候操作数栈是空的。然后java虚拟机会提供指令去将常量或者local variables中的值变量的值或者属性的值加载到操作数栈中。然后java虚拟机的其他指令从操作数栈获取操作数然后进行运算,再将结果放回到操作数栈中。操作数栈中还用于存放准备要传给其他方法的参数和从其他方法返回来的结果。

For example, the iadd instruction (§iadd) adds two int values together. It requires that the int values to be added be the top two values of the operand stack, pushed there by previous instructions. Both of the int values are popped from the operand stack. They are added, and their sum is pushed back onto the operand stack. Subcomputations may be nested on the operand stack, resulting in values that can be used by the encompassing computation.

例如:iadd指令用来对2个整数求和。他要求要相加的两个数必须是前面的指令添加到操作数栈栈顶的2个数。 相加时,这两个整数会从操作数栈中弹出。相加后的和会再次放回到操作数栈中。子计算可以嵌套在操作数堆栈上,从而生成计算需要使用的值。

Each entry on the operand stack can hold a value of any Java Virtual Machine type, including a value of type long or type double.

操作数栈上的每块空间都可以保存任何Java虚拟机类型的值,包括long或double类型的值。

Values from the operand stack must be operated upon in ways appropriate to their types. It is not possible, for example, to push two int values and subsequently treat them as a long or to push two float values and subsequently add them with an iadd instruction. A small number of Java Virtual Machine instructions (the dup instructions (§dup) and swap (§swap)) operate on run-time data areas as raw values without regard to their specific types; these instructions are defined in such a way that they cannot be used to modify or break up individual values. These restrictions on operand stack manipulation are enforced through class file verification (§4.10).

操作栈中的值必须用适合他们类型的指令来操作。比如你用iadd指令来操作两个float类型的值进行相加是不行的,因为iadd指令只能操作整数类型的值。只有少量Java虚拟机指令(dup指令(§dup)和swap(§swap))在操作运行时数据区的数据时将数据作为原始数据,而不考虑其类型;这些指令是这样被定义的:这些指令不能用于去执行修改和分解操作【1】。操作数栈的这些操作限制是通过对class文件进行verification的时候强制执行的【2】。

【1】:意思就是说有些指令只能用于复制,移动这样的操作,而不能进行修改某个值这样的操作
【2】:这种指令可以操作的类型是否与值的类型相匹配这样的检测都是在linking阶段的verification这步来进行的。后面会介绍linking相关的内容。

At any point in time, an operand stack has an associated depth, where a value of type long or double contributes two units to the depth and a value of any other type contributes one unit.

这段没啥作用,忽略即可。 就是说long和double在操作数栈中占2个单位,其他类型占1个单位

2.6.3. Dynamic Linking

Each frame (§2.6) contains a reference to the run-time constant pool (§2.5.5) for the type of the current method to support dynamic linking of the method code. The class file code for a method refers to methods to be invoked and variables to be accessed via symbolic references. Dynamic linking translates these symbolic method references into concrete method references, loading classes as necessary to resolve as-yet-undefined symbols, and translates variable accesses into appropriate offsets in storage structures associated with the run-time location of these variables.

一个方法对应一个栈帧,每一个栈帧中都包含一个指向运行时常量池中该栈帧所属方法的引用,从而支持到这个方法中的代码的动态链接【1】。一个方法会调用哪些其他的方法和属性都会在class字节码文件中通过符号引用的方式进行访问。动态链接将这些符号方法引用转换为具体的方法引用,根据需要加载类以解析尚未定义的符号,并将变量访问转换为与这些变量的运行时位置相关联的存储结构中的适当偏移量。

【1】:一个方法一个栈帧,但是栈帧中不放方法中要执行的指令,这些指令都放在运行时常量池中,所以栈帧中必须要有一个地方存放一个引用称为方法引用,这个引用指向运行时常量池中具体方法要执行的指令的地方。动态链接就是符号引用转为方法引用的过程。

This late binding of the methods and variables makes changes in other classes that a method uses less likely to break this code.

通过动态绑定可以保证类之间不相互影响,不会因为修改其他类中的变量或者方法而影响到自己。这句没啥用,可以忽略。

2.6.4. Normal Method Invocation Completion

A method invocation completes normally if that invocation does not cause an exception (§2.10) to be thrown, either directly from the Java Virtual Machine or as a result of executing an explicit throw statement. If the invocation of the current method completes normally, then a value may be returned to the invoking method. This occurs when the invoked method executes one of the return instructions (§2.11.8), the choice of which must be appropriate for the type of the value being returned (if any).

如果被调用的方法没有抛出异常,那么这个方法就会正常完成。正常执行完的方法就会向调用方返回一个值。当返回时,必须从2.11.8中的指令中选择一个适合的指令执行返回一个合适的类型的值。

The current frame (§2.6) is used in this case to restore the state of the invoker, including its local variables and operand stack, with the program counter of the invoker appropriately incremented to skip past the method invocation instruction. Execution then continues normally in the invoking method's frame with the returned value (if any) pushed onto the operand stack of that frame.

此段主要意思就是当前被调用的方法正常执行完成后,就会继续执行调用者的栈帧,然后把返回值放到栈帧中的操作数栈种,继续执行 。此段可忽略。

2.6.5. Abrupt Method Invocation Completion

A method invocation completes abruptly if execution of a Java Virtual Machine instruction within the method causes the Java Virtual Machine to throw an exception (§2.10), and that exception is not handled within the method. Execution of an athrow instruction (§athrow) also causes an exception to be explicitly thrown and, if the exception is not caught by the current method, results in abrupt method invocation completion. A method invocation that completes abruptly never returns a value to its invoker.

上面一段说的是调用的方法正常执行会返回一个值给调用者。这段的意思就是说,如果调用的方法执行过程中如果抛出异常,那么调用的方法会立刻完成,并且不给调用者返回任何值。

2.7. Representation of Objects(对象的表现形式)

The Java Virtual Machine does not mandate any particular internal structure for objects.

Java虚拟机不要求对象具有任何特定的内部结构【1】

In some of Oracle’s implementations of the Java Virtual Machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the heap for the object data.

有些Oracle实现的java虚拟机,一个引用指向句柄,这个句柄中有两个指针,一个指针指向方法区用于获得类的信息和方法,另一个指向分配在堆中的对象的数据。

这段说的就是网上说的对象访问的两种形式,句柄访问和直接指针访问。这里说的是句柄访问。上面英文说的就是句柄的方式,可以看下图;

直接指针方式如下:

2.8. Floating-Point Arithmetic

The Java Virtual Machine incorporates a subset of the floating-point arithmetic specified in IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std. 754-1985, New York).

这段可以忽略,就是说java虚拟机包含IEEE二进制浮点算法标准(ANSI/IEEE标准754-1985,纽约)中指定的浮点算法的子集。

2.8.1. Java Virtual Machine Floating-Point Arithmetic and IEEE 754

The key differences between the floating-point arithmetic supported by the Java Virtual Machine and the IEEE 754 standard are:
Java虚拟机支持的浮点算法与IEEE 754标准之间的主要区别是:

2.8.2. Floating-Point Modes

Every method has a floating-point mode, which is either FP-strict or not FP-strict. The floating-point mode of a method is determined by the setting of the ACC_STRICT flag of the access_flags item of the method_info structure (§4.6) defining the method. A method for which this flag is set is FP-strict; otherwise, the method is not FP-strict.

Note that this mapping of the ACC_STRICT flag implies that methods in classes compiled by a compiler in JDK release 1.1 or earlier are effectively not FP-strict.

We will refer to an operand stack as having a given floating-point mode when the method whose invocation created the frame containing the operand stack has that floating-point mode. Similarly, we will refer to a Java Virtual Machine instruction as having a given floating-point mode when the method containing that instruction has that floating-point mode.

If a float-extended-exponent value set is supported (§2.3.2), values of type float on an operand stack that is not FP-strict may range over that value set except where prohibited by value set conversion (§2.8.3). If a double-extended-exponent value set is supported (§2.3.2), values of type double on an operand stack that is not FP-strict may range over that value set except where prohibited by value set conversion.

In all other contexts, whether on the operand stack or elsewhere, and regardless of floating-point mode, floating-point values of type float and double may only range over the float value set and double value set, respectively. In particular, class and instance fields, array elements, local variables, and method parameters may only contain values drawn from the standard value sets.

暂不翻译,跟了解虚拟机没有太大作用,可以自行百度float-point

2.8.3. Value Set Conversion

An implementation of the Java Virtual Machine that supports an extended floating-point value set is permitted or required, under specified circumstances, to map a value of the associated floating-point type between the extended and the standard value sets. Such a value set conversion is not a type conversion, but a mapping between the value sets associated with the same type.

Where value set conversion is indicated, an implementation is permitted to perform one of the following operations on a value:

In addition, where value set conversion is indicated, certain operations are required:

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.

Not all values from an extended-exponent value set can be mapped exactly to a value in the corresponding standard value set. If a value being mapped is too large to be represented exactly (its exponent is greater than that permitted by the standard value set), it is converted to a (positive or negative) infinity of the corresponding type. If a value being mapped is too small to be represented exactly (its exponent is smaller than that permitted by the standard value set), it is rounded to the nearest of a representable denormalized value or zero of the same sign.

Value set conversion preserves infinities and NaNs and cannot change the sign of the value being converted. Value set conversion has no effect on a value that is not of a floating-point type.

暂不翻译,跟了解虚拟机没有太大作用,可以自行百度float-point

2.9. Special Methods

At the level of the Java Virtual Machine, every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name <init>. This name is supplied by a compiler. Because the name <init> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Instance initialization methods may be invoked only within the Java Virtual Machine by the invokespecial instruction (§invokespecial), and they may be invoked only on uninitialized class instances. An instance initialization method takes on the access permissions (JLS §6.6) of the constructor from which it was derived.

java代码编写的构造方法在java虚拟机中都会表现为一个叫做<init>的初始化方法(instance initialization method)。这个<init>是编译器提供的。因为<init>这个名字不是有效的标识符,因此不能在用Java编程语言编写的程序中直接使用。实例初始化方法(instance initialization method)只能通过invokespecial指令(§invokespecial)在Java虚拟机内调用,并且只能在未初始化的类实例上调用它们。这个<init>初始化方法才有构造方法的访问权限。

这一小节中说的实例初始化方法(instance initialization method)就是指的<init>方法,下面不在说明。

A class or interface has at most one class or interface initialization method and is initialized (§5.5) by invoking that method. The initialization method of a class or interface has the special name <clinit>, takes no arguments, and is void (§4.3.3).

一个类或接口最多有一个初始化方法(instance initialization method),并通过调用该方法进行初始化(§5.5)。 这个类或接口的初始化方法有个特殊的名字叫做<clinit>,它不接受任何参数,也没有返回值(§4.3.3)。

Other methods named <clinit> in a class file are of no consequence. They are not class or interface initialization methods. They cannot be invoked by any Java Virtual Machine instruction and are never invoked by the Java Virtual Machine itself.

这段可以忽略,意思就是说如果你的class文件中即便是出现了其他的叫做<clinit>名字的方法,也没有任何作用,虚拟机中的指令和虚拟机本身也不会执行它

<clinit>最多只能有一个,所以你class文件中再有多个<clinit>方法也没用。

In a class file whose version number is 51.0 or above, the method must additionally have its ACC_STATIC flag (§4.6) set in order to be the class or interface initialization method.

当一个class字节码文件中的版本号在51.0或者之上时,方法必须另外设置其ACC_静态标志(§4.6),才会成为类或接口初始化方法。

【1】:<clinit>叫做类和接口初始化方法,要与前面的<init>实例初始化方法区分开。类和接口初始化方法就是初始化static定义的东西。在代码中只有使用了static修饰了,那么才会被<clinit>执行。
具体会在jvm实现篇的讲解中详细说明

This requirement was introduced in Java SE 7. In a class file whose version number is 50.0 or below, a method named <clinit> that is void and takes no arguments is considered the class or interface initialization method regardless of the setting of its ACC_STATIC flag.

这个需求是在JavaSE7中引入的。在版本号为50.0或更低的class字节码文件中,只要你的<clinit>方法是一个不带参数的切没有返回值的方法,他就会被视为类或接口初始化方法,无论是否设置了ACC_STATIC

The name <clinit> is supplied by a compiler. Because the name <clinit> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java Virtual Machine; they are never invoked directly from any Java Virtual Machine instruction, but are invoked only indirectly as part of the class initialization process.

<clinit>这个名字是编译器提供的。因为<clinit>这个名字不是有效的标识符,因此不能在用Java编程语言编写的程序中直接使用。类和接口初始化方法(<clinit>)由Java虚拟机隐式调用;它们从不直接被任何Java虚拟机指令调用,而是作为类初始化过程的一部分间接调用。

A method is signature polymorphic if all of the following are true:
满足下面条件的方法就是一个signature(签名) polymorphic(多态)的方法

对上面这段详细说明一下:主要讲的是在java中有一种方法signature polymorphic。怎样才算是一个signature polymorphic方法,首先这个方法必须在MethodHandle这个类中定义,而且这个方法必须是native修饰的,并且参数必须是一个可变参数,又一个返回值。
ACC_VARARGS:就是指可变参数,当我们方法的参数是可变参数时,编译后的class文件就会有ACC_VARARGS这个标记了
ACC_NATIVE:当方法使用native修饰符时,编译后的class文件就会有ACC_NATIVE这个标记了。
我们现在看一下MethodHandle中已经定义的signature polymorphic方法



他们就都满足了上面说的条件。

In Java SE 8, the only signature polymorphic methods are the invoke and invokeExact methods of the class java.lang.invoke.MethodHandle.

在java8中只有MethodHandle类中的invoke 方法和invokeExact方法是signature polymorphic(看上面截图部分)

The Java Virtual Machine gives special treatment to signature polymorphic methods in the invokevirtual instruction (§invokevirtual), in order to effect invocation of a method handle. A method handle is a strongly typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation (§5.4.3.5), with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. See the java.lang.invoke package in the Java SE platform API for more information.

Java虚拟机在invokevirtual指令(§invokevirtual)中对 signature polymorphic方法进行了特殊处理,以实现方法句柄的调用。方法句柄(method handle)是一个强类型的可直接执行的引用,这个引用直接指向了方法,构造方法,字段,以及一些低级的操作,也就是说jvm可以通过方法句柄来调用方法,构造方法,字段等。就是说Method Handle这个类中提供的一些方法(如asType方法就是conversion功能,这句话就是api中直接引用的)。这些方法的详情可以查看java.lang.invoke包

这里对上面说的MethodHandle部分进行一个说明:
众所周知,Java从最初发布时就支持反射,通过反射可以在运行时获取类型信息,但其有个缺点就是执行速度较慢。于是从Java 7开始提供了另一套位于 java.lang.invoke中的API MethodHandle 。其与反射的作用类似,可以在运行时访问类型信息,但是据说其执行效率比反射更高,也被称为Java的 现代化反射

JDK 7 时新加入的 java.lang.invoke 包是 JSR 292 的一个重要组成部分,这个包的主要目的是在之前单纯依靠符号引用来确定调用的目标方法这条路之外,提供一种新的动态确定目标方法的机制,称为「方法句柄」(MethodHandle)。它的主要目的是为JVM设计的一套API,以支持其他JVM语言的反射能力,例如Groovy 、Scale、Kotlin 等。

与java.lang.reflecct包的区别

  • MethodHandle服务于所有java虚拟机上的语言,Reflection仅仅服务于java语言。
  • Reflection在模拟Java代码层次的调用,而MethodHandle在模拟字节码层次的方法调用。
  • Reflection是重量级,而MethodHandle是轻量级。
  • MethodHandle可以进行内联优化,Reflection完全没有

秒懂Java之方法句柄(MethodHandle) - it610.com

2.10. Exceptions

An exception in the Java Virtual Machine is represented by an instance of the class Throwable or one of its subclasses. Throwing an exception results in an immediate nonlocal transfer of control from the point where the exception was thrown.

java虚拟中中异常通过Throwable或者它的子类来表示。抛出一个异常后会导致从引发异常的地方立即进行控制权的转移【1】

【1】:就是发生异常后,后面的代码就不执行了。所谓控制权转移,就是交给专门的地方来处理这个异常,例如我们java中用catch来处理异常。

Most exceptions occur synchronously as a result of an action by the thread in which they occur. An asynchronous exception, by contrast, can potentially occur at any point in the execution of a program. The Java Virtual Machine throws an exception for one of three reasons:
大部分的异常都是与其所在的线程同步发生的。如果是一个异步异常就有可能发生在程序的任何地方。Java虚拟机引发异常的原因有三种:

A Java Virtual Machine may permit a small but bounded amount of execution to occur before an asynchronous exception is thrown. This delay is permitted to allow optimized code to detect and throw these exceptions at points where it is practical to handle them while obeying the semantics of the Java programming language.

Java虚拟机允许在抛出异步异常之前执行少量但有限制的处理。这种延迟允许优化代码在符合Java编程语言语义的情况下,在实际处理异常时检测并抛出这些异常。此段可以忽略

A simple implementation might poll for asynchronous exceptions at the point of each control transfer instruction. Since a program has a finite size, this provides a bound on the total delay in detecting an asynchronous exception. Since no asynchronous exception will occur between control transfers, the code generator has some flexibility to reorder computation between control transfers for greater performance. The paper Polling Efficiently on Stock Hardware by Marc Feeley, Proc. 1993 Conference on Functional Programming and Computer Architecture, Copenhagen, Denmark, pp. 179–187, is recommended as further reading. 不重要,可以不用管

Exceptions thrown by the Java Virtual Machine are precise: when the transfer of control takes place, all effects of the instructions executed before the point from which the exception is thrown must appear to have taken place. No instructions that occur after the point from which the exception is thrown may appear to have been evaluated. If optimized code has speculatively executed some of the instructions which follow the point at which the exception occurs, such code must be prepared to hide this speculative execution from the user-visible state of the program.

Java虚拟机抛出的异常必须是非常精确的:当控制权转移发生时,在抛出异常点之前执行的指令的所有效果必须看起来已经发生。在引发异常的点之后发生的任何指令似乎都没有经过计算。如果优化代码推测性地执行了异常发生点之后的某些指令,则必须准备好这样的代码,以便在程序的用户可见状态下隐藏这种推测性执行。这段不重要,可忽略

Each method in the Java Virtual Machine may be associated with zero or more exception handlers. An exception handler specifies the range of offsets into the Java Virtual Machine code implementing the method for which the exception handler is active, describes the type of exception that the exception handler is able to handle, and specifies the location of the code that is to handle that exception. An exception matches an exception handler if the offset of the instruction that caused the exception is in the range of offsets of the exception handler and the exception type is the same class as or a subclass of the class of exception that the exception handler handles. When an exception is thrown, the Java Virtual Machine searches for a matching exception handler in the current method. If a matching exception handler is found, the system branches to the exception handling code specified by the matched handler.

java虚拟机中每个方法都有零个或者多个异常处理器。异常处理器指定Java虚拟机代码中实现异常处理程序激活的方法的偏移量范围,描述异常处理程序能够处理的异常类型,并指定要处理该异常的代码的位置。如果导致异常的指令的偏移量在异常处理程序的偏移量范围内,并且异常类型与异常处理程序处理的异常类相同或是异常处理程序处理的异常类的子类,则异常与异常处理程序匹配。当抛出异常时,Java虚拟机在当前方法中搜索匹配的异常处理程序。如果找到匹配的异常处理程序,系统将分支到由匹配的处理程序指定的异常处理代码

If no such exception handler is found in the current method, the current method invocation completes abruptly (§2.6.5). On abrupt completion, the operand stack and local variables of the current method invocation are discarded, and its frame is popped, reinstating the frame of the invoking method. The exception is then rethrown in the context of the invoker's frame and so on, continuing up the method invocation chain. If no suitable exception handler is found before the top of the method invocation chain is reached, the execution of the thread in which the exception was thrown is terminated.

如果在当前方法中未找到此类异常处理程序,则当前方法调用会立刻完成 。完成后,当前方法的操作数栈和局部变量表将会被丢弃,当前的方法帧会被弹出,然后返回到这个方法的调用者所在的方法中帧中。然后这个异常会在调用者所在的方法中再次被抛出,然后看有么有对应的处理器,以此类推。如果在到达方法调用链顶部之前未找到合适的异常处理程序,则终止引发异常的线程的执行。

这段的意思就是:方法A调用方法B,如果方法B中的代码发生异常了,但是在方法B中没有找到处理这个异常的处理器,那么就会立刻终止B方法,然后回到方法A中,然后再看有没有处理这个异常的处理器,如果没有就再往上找。如果到最后也没找到那么代码就结束执行。

The order in which the exception handlers of a method are searched for a match is important. Within a class file, the exception handlers for each method are stored in a table (§4.7.3). At run time, when an exception is thrown, the Java Virtual Machine searches the exception handlers of the current method in the order that they appear in the corresponding exception handler table in the class file, starting from the beginning of that table.

方法的异常处理器的匹配顺序非常重要。在class字节码文件中,每个方法的异常处理器被存储在一个table中。在运行时,当抛出一个异常时,java虚拟机会从class字节码文件中的异常表里去寻找当前方法对应的异常处理器。

Note that the Java Virtual Machine does not enforce nesting of or any ordering of the exception table entries of a method. The exception handling semantics of the Java programming language are implemented only through cooperation with the compiler (§3.12        ). When class files are generated by some other means, the defined search procedure ensures that all Java Virtual Machine implementations will behave consistently.

java虚拟机不强制要求方法的异常表必须排序。Java编程语言的异常处理语义只能通过与编译器的合作来实现 。

2.11. Instruction Set Summary

A Java Virtual Machine instruction consists of a one-byte opcode specifying the operation to be performed, followed by zero or more operands supplying arguments or data that are used by the operation. Many instructions have no operands and consist only of an opcode.

java虚拟机指令由一个字节的操作码(opcode)再加上0个或者多个操作数(operands )组成。
大多数的指令没有操作码或者仅仅有一个操作码。

参考jvm实现中关于指令集篇章中,有详细解释

The number and size of the operands are determined by the opcode. If an operand is more than one byte in size, then it is stored in big-endian order - high-order byte first. For example, an unsigned 16-bit index into the local variables is stored as two unsigned bytes, byte1 and byte2, such that its value is (byte1 << 8) | byte2.

操作数的数目和大小由操作码决定(opcode)。如果一个操作数的大小超过了1个字节,那么它将以大端顺序存储也就是首先是存储高位字节。例如,一个16位的无符号索引值会被存储在两个无符号字节中,其值为(字节1<<8)|字节2。

这段话主要讲的是当操作数的值大于1个字节时,怎么存储它的值,涉及到存储,所以就要明白大端和小端。用10进制举个例子,比如把21存到内存,肯定是把十位数的2放到内存的最前面(内存中靠前的也称为低地址),然后把个位数的1放到后面的内存(靠后的内存称为高地址)。这种就叫做大端。相反的就叫做小端。下面在上一个图例,方便大家理解

The bytecode instruction stream is only single-byte aligned. The two exceptions are the lookupswitch and tableswitch instructions (§lookupswitch, §tableswitch), which are padded to force internal alignment of some of their operands on 4-byte boundaries.

字节码指令流(bytecode instruction stream)都是按照单字节来对齐。但是这里有两个例外是lookupswitch和tableswitch指令(§lookupswitch,§tableswitch),它们被填充以强制在4字节边界上对某些操作数进行内部对齐。

字节码由操作码+操作数组成。这个别乱了。关于字节对齐,可自行百度。对齐就是为了提高执行效率。

The decision to limit the Java Virtual Machine opcode to a byte and to forgo data alignment within compiled code reflects a conscious bias in favor of compactness, possibly at the cost of some performance in naive implementations. A one-byte opcode also limits the size of the instruction set. Not assuming data alignment means that immediate data larger than a byte must be constructed from bytes at run time on many machines.

决定将Java虚拟机操作码限制为一个字节,并放弃编译代码中的数据对齐,这反映出为了紧凑型而有意识地以牺牲一些性能做为代价。单字节操作码也会限制指令集的大小。不进行数据对齐的意思就是在很多机器中当运行时的数据大于一个字节时就必须按照字节为单位来构建。(不重要,不理解可以忽略,不影响你就jvm规范的理解)

2.11.1. Types and the Java Virtual Machine

Most of the instructions in the Java Virtual Machine instruction set encode type information about the operations they perform. For instance, the iload instruction (§iload) loads the contents of a local variable, which must be an int, onto the operand stack. The fload instruction (§fload) does the same with a float value. The two instructions may have identical implementations, but have distinct opcodes.

java虚拟机指令集中的大多数指令都会对他们要进行操作的数据类型的信息进行编码【1】。例如,iload指令的意思是从本地变量中读取数据,这个数据必须是int类型,然后把这个数据放到操作数栈中。fload指令就是要读取一个float类型的值。这两条指令可能具有相同的实现,但具有不同的操作码【2】。

【1】这句话的意思就是大多数的指令,通过指令本身就能看出他是用来操作什么类型数据的。比如iload通过首字母i,就知道它是用来读取int类型值的。这个i就是这句话中所谓的编码,千万别被编码这个词影响。

【2】何为2个指令有相同的实现,但是有不同的操作码。比如说你现在读取数据5和5.0,不管读那个值具体的操作的实现都是去内存读数的操作,所以被称为相同的实现,但是怎么知道我也读一个int类型的数还是float类型的数呢?这就需要不同的指令来实现,你用iload他就读int类型,你用fload他就读float类型,这个就是所谓不同的操作码。

这段话主要让你明白2个事情:第一个是:jvm中大多数的指令,都编码为了通过本身一眼就能看出是要对什么类型操作的指令,如:iload这就是编码后的样子,这样就可以通过指令本身看出是对int类型操作,这也就是所谓的助记符,也就是这句话中编码的意思。第二个就是:不同的类型的数据要用不同的操作码进行操作。

For the majority of typed instructions, the instruction type is represented explicitly in the opcode mnemonic by a letter: i for an int operation, l for longs for shortb for bytec for charf for floatd for double, and a for reference. Some instructions for which the type is unambiguous do not have a type letter in their mnemonic. For instance, arraylength always operates on an object that is an array. Some instructions, such as goto, an unconditional control transfer, do not operate on typed operands.

对于大多数类型化指令,指令要操作的类型会显示的在指令中通过一个字母来表示:i表示int,l表示long,s表示short,b表示byte,c表示char,f表示float,d表示double,a表示引用类型。 某些类型明确的指令的助记符中没有表示类型字母。例如,arraylength指令专门用来获取数组的。其他一些指令,例如goto,他们属于控制类指令,他们就不是用来操作与类型相关的操作数的。

Given the Java Virtual Machine's one-byte opcode size, encoding types into opcodes places pressure on the design of its instruction set. If each typed instruction supported all of the Java Virtual Machine's run-time data types, there would be more instructions than could be represented in a byte. Instead, the instruction set of the Java Virtual Machine provides a reduced level of type support for certain operations. In other words, the instruction set is intentionally not orthogonal. Separate instructions can be used to convert between unsupported and supported data types as necessary.

可忽略

Table 2.11.1-A summarizes the type support in the instruction set of the Java Virtual Machine. A specific instruction, with type information, is built by replacing the T in the instruction template in the opcode column by the letter in the type column. If the type column for some instruction template and type is blank, then no instruction exists supporting that type of operation. For instance, there is a load instruction for type intiload, but there is no load instruction for type byte.

2.11.1-A表格中列出了jvm指令集中支持类型操作的指令。如果表格中空着的,就说明指令不支持某个类型的操作。

Note that most instructions in Table 2.11.1-A do not have forms for the integral types bytechar, and short. None have forms for the boolean type. A compiler encodes loads of literal values of types byte and short using Java Virtual Machine instructions that sign-extend those values to values of type int at compile-time or run-time. Loads of literal values of types boolean and char are encoded using instructions that zero-extend the literal to a value of type int at compile-time or run-time. Likewise, loads from arrays of values of type booleanbyteshort, and char are encoded using Java Virtual Machine instructions that sign-extend or zero-extend the values to values of type int. Thus, most operations on values of actual types booleanbytechar, and short are correctly performed by instructions operating on values of computational type int.

注意,2.11.1-A表格中大多数的指令的byte,char,short部分都是空着的。并且表格中没有boolean类型。在编译时或者运行时,编译器会通过jvm指令把byte,short都转换为int。boolean和char也会转换为int。同样的,如果是byte,char,short,boolean类型的数组时也会转换为int数组。因此大多数boolean,byte,char,short类型的操作都会转换为int进行操作。

这段的意思就是jvm中(或者可以理解为java中)byte,short,boolean,char都会转换为int进行操作就行了。这也就是为什么java中两个short类型相加,结果为int的原因了。

Table 2.11.1-A. Type support in the Java Virtual Machine instruction set

opcode byte short int long float double char reference
Tipush bipush sipush
Tconst iconst lconst fconst dconst aconst
Tload iload lload fload dload aload
Tstore istore lstore fstore dstore astore
Tinc iinc
Taload baload saload iaload laload faload daload caload aaload
Tastore bastore sastore iastore lastore fastore dastore castore aastore
Tadd iadd ladd fadd dadd
Tsub isub lsub fsub dsub
Tmul imul lmul fmul dmul
Tdiv idiv ldiv fdiv ddiv
Trem irem lrem frem drem
Tneg ineg lneg fneg dneg
Tshl ishl lshl
Tshr ishr lshr
Tushr iushr lushr
Tand iand land
Tor 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 ireturn lreturn freturn dreturn areturn

这些指令的意思会在专门章节讲解。

The mapping between Java Virtual Machine actual types and Java Virtual Machine computational types is summarized by Table 2.11.1-B.

Certain Java Virtual Machine instructions such as pop and swap operate on the operand stack without regard to type; however, such instructions are constrained to use only on values of certain categories of computational types, also given in Table 2.11.1-B.

意思就是说:2.11.1-B表格中列出了jvm中语法中使用的类型和jvm指令在运算时实际使用的类型的映射关系(就好比之前说的我们代码语法使用的是short,但是jvm指令在操作两数相加时,都会吧short变为int进行运算,这个int就是jvm实际的运算类型,也就是computational type)。

某些Java虚拟机指令例如pop和swap他们在操作数栈上进行操作,这些指令不需要考虑类型;然而,此类指令仅限于在表2.11.1-B中给出的某些计算类型的值上使用。

Table 2.11.1-B. Actual and Computational types in the Java Virtual Machine

Actual type Computational type Category
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. Load and Store Instructions(读取和存储相关的指令)

The load and store instructions transfer values between the local variables (§2.6.1) and the operand stack (§2.6.2) of a Java Virtual Machine frame (§2.6):
加载和存储用于在jvm的帧中的本地变量表和操作数栈之间传输值,主要的作用和相关指令如下:

Instructions that access fields of objects and elements of arrays (§2.11.5) also transfer data to and from the operand stack.

访问对象属性的指令和访问数组元素的指令,这些指令也会把获取到的数据在操作数栈上来回传送。

Instruction mnemonics shown above with trailing letters between angle brackets (for instance, iload_<n>) denote families of instructions (with members iload_0iload_1iload_2, and iload_3 in the case of iload_<n>). Such families of instructions are specializations of an additional generic instruction (iload) that takes one operand. For the specialized instructions, the operand is implicit and does not need to be stored or fetched. The semantics are otherwise the same (iload_0 means the same thing as iload with the operand 0). The letter between the angle brackets specifies the type of the implicit operand for that family of instructions: for <n>, a nonnegative integer; for <i>, an int; for <l>, a long; for <f>, a float; and for <d>, a double. Forms for type int are used in many cases to perform operations on values of type bytechar, and short (§2.11.1).

这段单句翻译不好理解,所以整体讲一下具体意思,主要就是说上面开头讲的那些指令中,有一些是以_<n>结尾的,如iload_<n>,看到这样的要明白两个点:
第一:看到指令后面有_<n>这种符号就说明他指代的是好几个指令,以iload_<n>为例,实际上总会有 iload_0,iload_1,iload_2,iload_3三个指令。
第二:<n>表示的指令将来要操作的操作数的类型。<n>表示的是无符号整形,除了<n>还有<i>表示整形,<l>表示long型,<f>表示float型,<d>表示double型。例如iconst_<i>。具体的会在介绍这些指令时详细介绍。

2.11.3. Arithmetic Instructions(运算指令)

The arithmetic instructions compute a result that is typically a function of two values on the operand stack, pushing the result back on the operand stack. There are two main kinds of arithmetic instructions: those operating on integer values and those operating on floating-point values. Within each of these kinds, the arithmetic instructions are specialized to Java Virtual Machine numeric types. There is no direct support for integer arithmetic on values of the byteshort, and char types (§2.11.1), or for values of the boolean type; those operations are handled by instructions operating on type int. Integer and floating-point instructions also differ in their behavior on overflow and divide-by-zero. The arithmetic instructions are as follows:

运算指令用来计算操作数栈上的2个值,然后再把运算结果放到操作数栈上【1】。主要有两种类型的运算指令:对整数值的运算指令和对浮点值的运算指令。在每种类型中,算术指令都专门用于Java虚拟机numeric 类型【2】。对于 byteshort, and char类型 的值(§2.11.1)或布尔类型的值,不直接支持整数算术;这些操作由在int类型上操作的指令处理(byte,short,char,boolean都转换为int,然后使用处理int类型的指令来处理)。整数和浮点指令在溢出和被零除时的行为也不同。运算指令如下:

【1】:例如方法中有一个2+3的操作,会把2和3分别放到操作数栈,然后使用运算指令时,会再从操作数栈中把2和3分别拿出来(也就是出栈,这时候操作数栈中空了),然后运算完成后,将结果在放回到操作数栈中,这时候结果就在栈顶了,最后再有其他命令来使用这个结果,比如赋值给某个变量x。

【2】什么是numeric看前面2.3. Primitive Types and Values。里面讲解了。

The Java Virtual Machine does not indicate overflow during operations on integer data types. The only integer operations that can throw an exception are the integer divide instructions (idiv and ldiv) and the integer remainder instructions (irem and lrem), which throw an ArithmeticException if the divisor is zero.

Java虚拟机在对整型数据类型进行操作时不会指示溢出。唯一可以引发异常的整数操作是整数除法指令(idiv和ldiv)和整数余数指令(irem和lrem),如果除数为零,则会引发算术异常

Java Virtual Machine operations on floating-point numbers behave as specified in IEEE 754. In particular, the Java Virtual Machine requires full support of IEEE 754 denormalized floating-point numbers and gradual underflow, which make it easier to prove desirable properties of particular numerical algorithms.

Java虚拟机对浮点数的操作按照IEEE 754中的规定进行。特别是,Java虚拟机需要完全支持IEEE 754非规范化浮点数和渐进下溢,这使得更容易证明特定数值算法的理想特性。可忽略

The Java Virtual Machine requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one having a least significant bit of zero is chosen. This is the IEEE 754 standard's default rounding mode, known as round to nearest mode.

Java虚拟机要求浮点运算的行为就像每个浮点运算符将其浮点结果四舍五入到结果精度一样。不精确的结果必须四舍五入到最接近无限精确结果的可表示值;如果两个最近的可表示值相等接近,则选择具有最低有效位零的值。这是IEEE 754标准的默认舍入模式,称为舍入到最近模式。

The Java Virtual Machine uses the IEEE 754 round towards zero mode when converting a floating-point value to an integer. This results in the number being truncated; any bits of the significand that represent the fractional part of the operand value are discarded. Round towards zero mode chooses as its result the type's value closest to, but no greater in magnitude than, the infinitely precise result.

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

The Java Virtual Machine's floating-point operators do not throw run-time exceptions (not to be confused with IEEE 754 floating-point exceptions). An operation that overflows produces a signed infinity, an operation that underflows produces a denormalized value or a signed zero, and an operation that has no mathematically definite result produces NaN. All numeric operations with NaN as an operand produce NaN as a result.

Java虚拟机的浮点运算符不会引发运行时异常(不要与IEEE 754浮点异常混淆)。溢出的运算产生有符号无穷大,下溢的运算产生非规范化值或有符号零,没有数学上确定结果的运算产生NaN。所有以NaN作为操作数的数值运算都会产生NaN作为结果。

Comparisons on values of type long (lcmp) perform a signed comparison. Comparisons on values of floating-point types (dcmpgdcmplfcmpgfcmpl) are performed using IEEE 754 nonsignaling comparisons.

对long(lcmp)类型的值进行比较时,会执行有符号比较。浮点类型(dcmpg、dcmpl、fcmpg、fcmpl)的值的比较使用IEEE 754非签名比较来执行。

2.11.4. Type Conversion Instructions(类型转换指令)

The type conversion instructions allow conversion between Java Virtual Machine numeric types. These may be used to implement explicit conversions in user code or to mitigate the lack of orthogonality in the instruction set of the Java Virtual Machine.

类型转换指令允许在Java虚拟机numeric类型之间进行转换【1】。这些可用于实现用户代码中的显式转换,或缓解Java虚拟机指令集中缺乏正交性的问题。

【1】什么是numeric类型,看上面2.3. Primitive Types and Values

The Java Virtual Machine directly supports the following widening numeric conversions:
java虚拟机支持下面的numeric的类型转换:

The widening numeric conversion instructions are i2li2fi2dl2fl2d, and f2d. The mnemonics for these opcodes are straightforward given the naming conventions for typed instructions and the punning use of 2 to mean "to." For instance, the i2d instruction converts an int value to a double. (这句就是将指令中2还有前后字母的意思,可以忽略。)

类型转换的指令有:i2l,i2f,i2d,l2f,l2d,f2d。

Most widening numeric conversions do not lose information about the overall magnitude of a numeric value. Indeed, conversions widening from int to long and int to double do not lose any information at all; the numeric value is preserved exactly. Conversions widening from float to double that are FP-strict (§2.8.2) also preserve the numeric value exactly; only such conversions that are not FP-strict may lose information about the overall magnitude of the converted value.

大多数的数值转换不会丢失有关数值整体大小的信息。事实上,从int到long和int到double的转换根本不会丢失任何信息;数值被准确地保留下来。从浮点扩展到双精度的FP严格转换(§2.8.2)也精确保留数值;只有非FP严格的转换可能会丢失有关转换值总体大小的信息。

Conversions from int to float, or from long to float, or from long to double, may lose precision, that is, may lose some of the least significant bits of the value; the resulting floating-point value is a correctly rounded version of the integer value, using IEEE 754 round to nearest mode

从int到float,或从long到float,或从long到double的转换可能会丢失精度,也就是说,可能会丢失一些值的最低有效位;生成的浮点值是整数值的正确舍入版本,使用IEEE 754舍入到最近模式。

Despite the fact that loss of precision may occur, widening numeric conversions never cause the Java Virtual Machine to throw a run-time exception (not to be confused with an IEEE 754 floating-point exception).

尽管可能会发生精度损失,但不断扩大的数值转换不会导致Java虚拟机抛出运行时异常(不要与IEEE 754浮点异常混淆)。

A widening numeric conversion of an int to a long simply sign-extends the two's-complement representation of the int value to fill the wider format. A widening numeric conversion of a char to an integral type zero-extends the representation of the char value to fill the wider format.

此段不重要,忽略

Note that widening numeric conversions do not exist from integral types bytechar, and short to type int. As noted in §2.11.1, values of type bytechar, and short are internally widened to type int, making these conversions implicit.

byte,char,short之间的转换是不存在的。因为他们都是先会自动隐式转换为int。

The Java Virtual Machine also directly supports the following narrowing numeric conversions:
java虚拟机也支持向下转换

注意:narrowing numeric和widening numeric。这两个其实就是我们平时说的,基本数据类型转换时,向上转换和向下转换的意思。例如4个字节的int转换为8个字节的long就是widening,long转int就是narrowing,因为所占的内存不一样,就会导致信息完整性,也就是数值准确度的问题。

The narrowing numeric conversion instructions are i2bi2ci2sl2if2if2ld2id2l, and d2f. A narrowing numeric conversion can result in a value of different sign, a different order of magnitude, or both; it may thereby lose precision.

A narrowing numeric conversion of an int or long to an integral type T simply discards all but the n lowest-order bits, where n is the number of bits used to represent type T. This may cause the resulting value not to have the same sign as the input value.

向下转换的指令有:i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f。这些转换就会丢失精度。
将int或long缩小为整数类型T的数值转换只会丢弃除n个最低阶位以外的所有位,其中n是用于表示类型T的位数。这可能会导致结果值与输入值的符号不同

In a narrowing numeric conversion of a floating-point value to an integral type T, where T is either int or long, the floating-point value is converted as follows:
在浮点值到整数类型T转换中,其中T为int或long,转换如下:

A narrowing numeric conversion from double to float behaves in accordance with IEEE 754. The result is correctly rounded using IEEE 754 round to nearest mode. A value too small to be represented as a float is converted to a positive or negative zero of type float; a value too large to be represented as a float is converted to a positive or negative infinity. A double NaN is always converted to a float NaN.

从双精度到浮点精度的数值转换符合IEEE 754。使用IEEE 754四舍五入至最接近模式正确舍入结果。太小而无法表示为浮点的值被转换为浮点类型的正零或负零;太大而无法表示为浮点的值将转换为正无穷大或负无穷大。双NaN始终转换为浮点NaN。

Despite the fact that overflow, underflow, or loss of precision may occur, narrowing conversions among numeric types never cause the Java Virtual Machine to throw a run-time exception (not to be confused with an IEEE 754 floating-point exception).

尽管可能发生溢出、下溢或精度损失,但缩小数值类型之间的转换范围不会导致Java虚拟机抛出运行时异常(不要与IEEE 754浮点异常混淆)。

2.11.5. Object Creation and Manipulation(对象的创建和操作指令)

Although both class instances and arrays are objects, the Java Virtual Machine creates and manipulates class instances and arrays using distinct sets of instructions:

尽管类实例和数组都是对象,但Java虚拟机使用不同的指令集创建和操作类实例和数组:

2.11.6. Operand Stack Management Instructions(操作数栈管理指令)

A number of instructions are provided for the direct manipulation of the operand stack: poppop2dupdup2dup_x1dup2_x1dup_x2dup2_x2swap.

用来对操作数栈进行操作的指令如下:poppop2dupdup2dup_x1dup2_x1dup_x2dup2_x2swap.

这些指令就是例如出栈,入栈的操作指令

2.11.7. Control Transfer Instructions(控制传输指令)

The control transfer instructions conditionally or unconditionally cause the Java Virtual Machine to continue execution with an instruction other than the one following the control transfer instruction. They are:

  • Conditional branch:
    ifeqifneifltifleifgtifgeifnullifnonnullif_icmpeqif_icmpneif_icmpltif_icmpleif_icmpgt if_icmpgeif_acmpeqif_acmpne.

  • Compound conditional branch: tableswitchlookupswitch.

  • Unconditional branch: gotogoto_wjsrjsr_wret.

控制传输指令可以控制Java虚拟机是继续执行后面的指令还是跳到其他地方执行其他的指令。控制传输指令如下:

  • 含有条件分支的控制指令
    ifeqifneifltifleifgtifgeifnullifnonnullif_icmpeqif_icmpneif_icmpltif_icmpleif_icmpgt if_icmpgeif_acmpeqif_acmpne.
  • 含有复合条件分支的控制指令
    tableswitchlookupswitch.
  • 不含有条件建的控制指令
    gotogoto_wjsrjsr_wret.

分支的意思就是满足不同条件跳到不同位置然后继续执行指令

The Java Virtual Machine has distinct sets of instructions that conditionally branch on comparison with data of int and reference types. It also has distinct conditional branch instructions that test for the null reference and thus it is not required to specify a concrete value for null (§2.4).

这段单句不好翻译,具体意思就是,在判断是否为null的时候,不用单独为null指定一个值,jvm有专门的指令来判断是否为null

Conditional branches on comparisons between data of types booleanbytechar, and short are performed using int comparison instructions (§2.11.1). A conditional branch on a comparison between data of types longfloat, or double is initiated using an instruction that compares the data and produces an int result of the comparison (§2.11.3). A subsequent int comparison instruction tests this result and effects the conditional branch. Because of its emphasis on int comparisons, the Java Virtual Machine provides a rich complement of conditional branch instructions for type int.

这段主要讲在条件分支中使用byte,char,boolean,long,float,double类型的数据当作条件进行比较时,都会用到int相关的指令。并且比较后的结果也会是一个int类型的。因为jvm对int提供了拱了丰富的条件分支指令。忽略也行,不是重点。

2.11.8. Method Invocation and Return Instructions(方法调用和返回指令)

The following five instructions invoke methods:

下面五个指令是用来进行方法调用的

The method return instructions, which are distinguished by return type, are ireturn (used to return values of type booleanbytecharshort, or int), lreturnfreturndreturn, and areturn. In addition, the return instruction is used to return from methods declared to be void, instance initialization methods, and class or interface initialization methods.

方法返回指令根据返回的类型分为  ireturn (used to return values of type booleanbytecharshort, or int), lreturnfreturndreturn, and areturn。此外,return指令用于从声明为void的方法、实例初始化方法以及类或接口初始化方法返回。

2.11.9. Throwing Exceptions(抛出异常)

An exception is thrown programmatically using the athrow instruction. Exceptions can also be thrown by various Java Virtual Machine instructions if they detect an abnormal condition.

异常通过athrow指令来抛出。如果Java虚拟机中的指令检测到异常情况,它们也会通过athrow来抛出异常

2.11.10. Synchronization(同步)

The Java Virtual Machine supports synchronization of both methods and sequences of instructions within a method by a single synchronization construct: the monitor.

java虚拟机支持方法和和代码块的同步【1】,主要的实现方式就是通过一个称为monitor的同步结构。

【1】方法同步就是在方法上使用synchronized关键字,代码块同步就是将方法中的某段代码用synchronized{}包裹起来

Method-level synchronization is performed implicitly, as part of method invocation and return (§2.11.8). A synchronized method is distinguished in the run-time constant pool's method_info structure (§4.6) by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.

方法级的同步(在方法上直接使用synchronized)作为方法调用和返回的一部分被隐式执行(§2.11.8)。区别是否是一个同步方法主要通过运行时常量池中的 method_info这个结构中的ACC_SYNCHRONIZED这个flag来区分。当调用一个设置了ACC_SYNCHRONIZED的方法时,正在执行的线程就会进入到一个monitor,然后调用方法,当调用方法结束就会突出monitor。当正在执行的线程拥有一个monitor时,其他的线程就不能在进入这个monitor了。如果在调用一个同步发方法时(含有synchronized的方法)发生了异常,并且方法本身并没有处理这个异常,那么异常在重新向外抛出之前会先自动退出monitor

Synchronization of sequences of instructions is typically used to encode the synchronized block of the Java programming language. The Java Virtual Machine supplies the monitorenter and monitorexit instructions to support such language constructs. Proper implementation of synchronized blocks requires cooperation from a compiler targeting the Java Virtual Machine (§3.14).

Java代码时使用同步块时,jvm就会对代码对应的指令进行同步处理。java虚拟机提供了monitorenter and monitorexit 指令来进行支持。如何正确的实现同步需要java虚拟机编译器的合作(§3.14)。

Structured locking is the situation when, during a method invocation, every exit on a given monitor matches a preceding entry on that monitor. Since there is no assurance that all code submitted to the Java Virtual Machine will perform structured locking, implementations of the Java Virtual Machine are permitted but not required to enforce both of the following two rules guaranteeing structured locking. Let T be a thread and M be a monitor. Then:

  1. The number of monitor entries performed by T on M during a method invocation must equal the number of monitor exits performed by T on M during the method invocation whether the method invocation completes normally or abruptly.

  2. At no point during a method invocation may the number of monitor exits performed by T on M since the method invocation exceed the number of monitor entries performed by T on M since the method invocation.

结构化锁定是指在方法调用期间,给定监视器上的每个出口都与该监视器上的前一个入口相匹配的情况。由于无法保证提交给Java虚拟机的所有代码都将执行结构化锁定,因此允许Java虚拟机的实现,但不需要强制执行以下两个保证结构化锁定的规则。假设T为线程,M为监视器。然后:

  1. 无论方法调用是正常完成还是突然完成,T on M在方法调用期间执行的监视器条目数必须等于T on M在方法调用期间执行的监视器退出数。
  2. 在方法调用期间,T在M上执行的监视器退出的数量在任何时候都不可能超过T在M上执行的监视器条目的数量。请注意,Java虚拟机在调用同步方法时自动执行的监视器入口和出口被认为是在调用方法的调用过程中发生的。

Note that the monitor entry and exit automatically performed by the Java Virtual Machine when invoking a synchronized method are considered to occur during the calling method's invocation.

注意在同步方法的调用时,jvm虚拟机自动执行monitor的进入和退出。

2.12. Class Libraries

The Java Virtual Machine must provide sufficient support for the implementation of the class libraries of the Java SE platform. Some of the classes in these libraries cannot be implemented without the cooperation of the Java Virtual Machine.

Java虚拟机必须为Java SE平台类库的实现提供足够的支持。如果没有Java虚拟机的合作,这些库中的一些类就无法实现。

Classes that might require special support from the Java Virtual Machine include those that support:

类有可能需要java虚拟机提供下面的支持:

The list above is meant to be illustrative rather than comprehensive. An exhaustive list of these classes or of the functionality they provide is beyond the scope of this specification. See the specifications of the Java SE platform class libraries for details.

上面的列出的不是所有的,只是为了说明用。把他们它们提供的功能都列出来超出了本规范的范围。关于这部分详细内容可以查阅Java SE platflorm class libraries

2.13. Public Design, Private Implementation

Thus far this specification has sketched the public view of the Java Virtual Machine: the class file format and the instruction set. These components are vital to the hardware-, operating system-, and implementation-independence of the Java Virtual Machine. The implementor may prefer to think of them as a means to securely communicate fragments of programs between hosts each implementing the Java SE platform, rather than as a blueprint to be followed exactly.

到目前为止,本规范已经讲述了Java虚拟机2个部分:类文件格式和指令集。这些组件对于Java虚拟机的硬件、操作系统和实现独立性至关重要。实现者可能更愿意将它们视为在每个实现JavaSE平台的主机之间安全地通信程序片段的一种方法,而不是将其视为要严格遵循的蓝图。(没啥作用,可以忽略)

It is important to understand where the line between the public design and the private implementation lies. A Java Virtual Machine implementation must be able to read class files and must exactly implement the semantics of the Java Virtual Machine code therein. One way of doing this is to take this document as a specification and to implement that specification literally. But it is also perfectly feasible and desirable for the implementor to modify or optimize the implementation within the constraints of this specification. So long as the class file format can be read and the semantics of its code are maintained, the implementor may implement these semantics in any way. What is "under the hood" is the implementor's business, as long as the correct external interface is carefully maintained.

理解公共设计和私有实现之间的界限是非常重要的【1】。java虚拟机的实现必须要能读取class字节码文件并且必须准确地实现其中Java虚拟机代码的语义。实现这一点的一种方法是将此文档作为规范,并按字面意思实现该规范。但是,对于实现者来说,在本规范的约束范围内修改或优化实现也是完全可行和可取的。只要可以读取类文件格式并维护其代码的语义,实现者就可以以任何方式实现这些语义。只要仔细维护正确的外部接口,“幕后”是实现者的事情。

【1】主要的意思就是说这个文档是规范,它属于public desgin,也就是负责宏观上应该满足的一些东西,关于这些东西具体怎么个实现法,就是所谓的private implement,也就是虚拟机实现者自己去完成就行。

There are some exceptions: debuggers, profilers, and just-in-time code generators can each require access to elements of the Java Virtual Machine that are normally considered to be “under the hood.” Where appropriate, Oracle works with other Java Virtual Machine implementors and with tool vendors to develop common interfaces to the Java Virtual Machine for use by such tools, and to promote those interfaces across the industry.

这里也有一些例外:调试器、探查器和即时代码生成器都可能需要访问Java虚拟机中通常被视为“隐藏”的元素,Oracle与其他Java虚拟机实现者和工具供应商合作,开发Java虚拟机的通用接口供此类工具使用,并在整个行业推广这些接口。

The implementor can use this flexibility to tailor Java Virtual Machine implementations for high performance, low memory use, or portability. What makes sense in a given implementation depends on the goals of that implementation. The range of implementation options includes the following:

实现者可以利用这种灵活性来定制Java虚拟机实现,以实现高性能、低内存使用或可移植性 。具体可以实现的的内容选项如下:

The existence of a precisely defined virtual machine and object file format need not significantly restrict the creativity of the implementor. The Java Virtual Machine is designed to support many different implementations, providing new and interesting solutions while retaining compatibility between implementations.

精确定义的虚拟机和对象文件格式不需要限制实现者的创造力。Java虚拟机旨在支持许多不同的实现,提供新的有趣的解决方案,同时保持实现之间的兼容性。( 这些可以忽略不看。不重要。)

[The Java8 Virtual Machine Specification述]Chapter2相关推荐

  1. Initialization in《The Java® Virtual Machine Specification Java SE 7 Edition》

    类(如果无特殊说明,本文中的"类"表示类和接口,下同)的初始化主要包括初始化的同步及执行其初始化方法<clinit>. 在以下几种情况下会触发类的初始化: (1)执行J ...

  2. [转]Windows Server 2012 和 System Center 2012 SP1,Virtual Machine Manager 中启用的软件定义的网络...

    消除障碍,实现云环境的灵活性.高效性和多租户功能 当 我们与客户谈论其数据中心时,我们发现虚拟化并未能充分发挥其潜能.客户对计算机虚拟化的优势大为认可,但他们希望能够获得更高程度的 IT 灵活性.客户 ...

  3. Windows Server 2012 和 System Center 2012 SP1,Virtual Machine Manager 中启用的软件定义的网络

    消除障碍,实现云环境的灵活性.高效性和多租户功能 当我们与客户谈论其数据中心时,我们发现虚拟化并未能充分发挥其潜能.客户对计算机虚拟化的优势大为认可,但他们希望能够获得更高程度的 IT 灵活性.客户尤 ...

  4. Windows Server 2012 和 System Center 2012 SP1,Virtual Machine Manager 中启用的软件定义的网络...

    消除障碍,实现云环境的灵活性.高效性和多租户功能 当我们与客户谈论其数据中心时,我们发现虚拟化并未能充分发挥其潜能.客户对计算机虚拟化的优势大为认可,但他们希望能够获得更高程度的 IT 灵活性.客户尤 ...

  5. 下载eclipse出现a java_打开Eclipse弹出“No java virtual machine was found...的解决方法

    今天准备用Eclipse抓取Android应用崩溃log,打开Eclipse时发现运行不了有以下弹框 A Java Runtime Environment(JRE) or Java Developme ...

  6. The HipHop Virtual Machine

    目前Facebook已将该HipHop虚拟机开源,源代码发布在GitHub上.关于该工具的技术原理在Facebook的开发者页面上有一篇详细的文章介绍,查看这里. 如果看不到的可以看下面的转载: We ...

  7. Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)...

    <Windows Azure Platform 系列文章目录> 注意:本文介绍的是Global Azure (http://www.windowsazure.com),如果你使用的是由世纪 ...

  8. eclipse failed to create the java virtual machine 问题图文解析

    eclipse failed to create  the java virtual  machine 解决方法: 1.问题现象 2.java虚拟机初始化失败!寻找eclipse解压路径 3.寻找ec ...

  9. [New Portal]Windows Azure Virtual Machine (8) Virtual Machine高可用(上)

    <Windows Azure Platform 系列文章目录> 我们之前介绍Windows Azure Virtual Machine的博文中不难发现,一台Vitual Machine对应 ...

最新文章

  1. sklearn中的交叉验证(Cross-Validation)
  2. 知道python网课答案_Python程序设计答案
  3. JavaWeb:脚本标识
  4. 【BZOJ1500】[NOI2005]维修数列 Splay
  5. 演练 实现等腰三角形
  6. 极速理解设计模式系列:4.原型模式(Prototype Pattern)
  7. 方舟编译器编译linux,方舟编译器环境配置
  8. python绘制相频特性曲线_用Python绘制音乐图谱
  9. KMPlayer如何设置H.264硬解
  10. 蚁群算法(实验分析)
  11. 2021-05-05 数组、 元组、字典、字符串常见操作
  12. 网易有道精品课好在哪里?有知道的大家说一说
  13. macbook自带python保存文件夹_在mac下查找python包存放路径site-packages的实现方法 在Mac系统下python如何安装第三方函数库?...
  14. opencv+hough直线检测+fitline直线拟合
  15. note20220411
  16. 【BW系列】SAP 讲讲BW/4 HANA和BW on HANA的区别
  17. 【小程序】小程序托管平台的功能展望与想法
  18. 惠普电脑BIOS设置图文详细介绍
  19. 生活学习常用软件介绍下载
  20. HTML页面编写中常遇到的bug

热门文章

  1. 为什么国企要加快推进数字化转型?
  2. 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录(转)
  3. 新手必学的20个人像摄影构图法
  4. dolphinscheduler 3.0.1 项目管理(二):工作流定义(上)
  5. Ubuntu的man手册中英文切换
  6. Spring Boot 之 Spring Data JPA(一)
  7. 网鼎杯2022青龙组
  8. 揭秘:日赚千元的冷门暴利项目,这个产品99%的人不知道
  9. [LoadRunner]LR性能测试结果样例分析
  10. 微软气坏了!Windows 惨遭抄袭,这款系统简直超越正品