第二章. Java虚拟机的结构
这篇文章定义了一个抽象的java虚拟机。不包含任何的具体实现。
为了正确地实现一个java虚拟机,你只需要正确地解析class文件并且正确地执行文件定义的操作。不属于java虚拟机定义的实现细节将不必要地限制实现者的创造力。比如,运行时数据区的内存布局,使用的垃圾回收算法,以及任何对java虚拟机指令的优化(比如,将它们翻译成机器代码)都由实现者自行决定。
本规范所有对Unicode的引用都针对Unicode6.0.0版本。 http://www.unicode.org/.
2.1 Class文件格式
JVM执行的代码使用一种硬件,操作系统独立的二进制格式,一般(不是必须)存放在一个叫做class的文件格式中。class文件精确地定义了如何表示一个类或者接口,包括字节序等细节。
2.2 数据类型
JVM操作两种类型的数据,primitive和reference。
JVM期望在run time之前完成几乎所有的类型检查,一般由编译器完成而不是JVM。primitive的值不需要被标记或者在运行时被检查来确认它们的类型,或者与reference类型的值区分。取而代之,通过使用为了操作特定类型而设计的JVM指令集来区分操作数的类型。比如,iadd, ladd, fadd, dadd。
JVM明确支持对象。对象是动态分配的class实例或者数组。指向对象的引用被认为是JVM的reference类型。引用类型的值可以认为是指向对象的指针。对一个对象的多个引用是可以存在的。
2.3 Primitive类型与值
JVM支持三种primitive类型,分别是数字类型,布尔类型和returnAddress类型。
数字类型包括整数和浮点数。
整数包括:
- byte,其值为8位有符号的二进制补码(Two's complement)整数,默认值为0。
- short,16位,0
- int, 32位, 0
- long, 64位,0
- char, 16位无符号, UTF-16,默认值\u0000
浮点数包括 - float,float value set and float-extended-exponent value set, 默认值+0
- double, double value set and double-extended-exponent value set, 默认值+0
布尔类型,默认false
returnAddress类型是指向JVM指令的操作码的指针。primitive类型中,只有returnAddress没有与java编成语言类型直接关联。
2.3.1 整数
- byte: [-27~27-1]
- char: [0, 28-1]
2.3.2 浮点数
IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std. 754-1985, New York).
IEEE 754: 正负数字,正负0,正负无穷,NaN
所有的JVM必须实现float value set和double value set。可以实现float-extended-exponent value sets和double-extended-exponent value set。
有限非零的浮点数的表示公式:s ⋅ m ⋅ 2(e − N + 1)
s = +1 or -1
0 < m <= 2N
-(2K-1-2) <= e <= (2K-1-1)
N and K depend on the value set
normalized value: m >= 2N-1
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 |
extended value set拥有更高的精度
float value set的元素正好是用IEEE 754可以表示的值,除了NaN(IEEE中有224-2个不同的NaN表示)。double value set同理。
float-extended-exponent value set与IEEE 754 single extended不是对应的,double同理。本规范不指定任何特定的对于float-point value sets的表示方式,除了float-point values必须可以被class文件格式表示。
float, float-extended-exponent, double, and double-extended-exponent value sets不是类型。使用float value set中的元素来表示float类型的一个值总是正确的。在特定的上下文,使用float-extended-exponent value set的元素作为float类型的值的实现是可以的。
除了NaNs,float-point value sets是有序(ordered)的。
+0 equals -0, 1.0/0.0 = +inf, 1.0/-0.0 = -inf
NaN is unordered。test for numerical equality is false when either operand is NaN
2.3.3 returnAddress
returnAddress类型被用于jsr, ret和jsr_w指令。没有对应的java编程类型。
2.3.4 boolean
JVM没有专用于操作boolean类型的指令。java编程语言中对boolean的操作在JVM中被转化为对JVM int类型的操作。
JVM支持boolean数组,使用newarray指令创建。boolean数组使用byte数组指令baload,bastore访问和修改。
Oracle的JVM实现中,对于boolean数组,每个boolean元素使用使用一个byte表示。
编译器将Java language的boolean类型映射为JVM的int类型。
2.4 Reference Type and Values
三种引用类型,class, array, interface。引用的值是动态创建的类的实例或者数组,或者分别实现了接口的类实例或者数组。
array类型由一维的component type组成(长度不由component type给出)。component type可以是array类型。the component type of a component type and so on, 最终的component type不是array类型,该类型称为该array type的element type。element type可以是class, primitive, interface。
reference类型的默认值是null。
规范不指定null的编码值。
2.5 Run-Time Data Areas
JVM定义了多种run-time data areas。一些运行时数据区在JVM启动和结束分别创建销毁,其他数据区每个线程独立分配。线程的运行时数据区在线程创建和退出时创建和销毁。
2.5.1 The pc Register
JVM支持同时执行多个线程。每个JVM线程具有自己的pc register。任何时刻,每一个JVM线程都在执行单个方法的代码,该方法称为current method for that thread。如果那个方法不是native的,pc register包含当前被执行的JVM instruction的地址。如果是native的,pc register的值undefined。JVM的pc register具有足够的宽度来存储returnAddress或者特定平台的native pointer。
2.5.2 JVM Stacks
每一个JVM thread有一个私有的JVM stack,stack与thread一同创建。JVM stack存储frame。JVM Stack与传统语言,比如c的stack类似:存储local variable和partial result,用于方法调用和返回。除了push和pop frame,JVM Stack永远不会被直接操作,所以frame可能被分配在heap上。JVM Stack的memory不必连续。
规范允许JVM Stack的size是固定的或者动态的。如果是固定的,每个thread创建时可以独立选择stack的size。
JVM的实现需要允许programmer和user选择stack的size,或者maximum size与minimum size。
以下异常情况与JVM stack相关:
- 如果计算需要stack size超过限制,JVM抛出StackOverflowError
- 对于动态stack,如果增加时,没有足够的memory,或者创建时,没有足够的memory,JVM抛出OutOfMemoryError。
2.5.3 Heap
所有JVM thread共享JVM Heap。所有的class实例和arrays都从heap这个run-time data area分配内存。
heap在JVM启动时创建。heap存储的objects由GC自动管理。objects永远不需要明确释放。JVM不假设GC的类型,GC技术由实现者根据自身系统要求自行选择。heap可以是fixed和dynamic。heap的内存不需要连续。
heap的size或者range可以指定。
以下异常情况与heap相关:
- 如果计算需要的heap空间多于GC能够提供的,抛出OutOfMemoryError
2.5.5 Method Area
JVM thread共享method area。