Subsections
2 Compiling for the Java Virtual Machine
2.2 Use of Constants, Local Variables, and Control Constructs
2.4 Accessing the Runtime Constant Pool
2.8 Working with Class Instances
2.11 Operationson the Operand Stack
2.12 Throwingand Handling Exceptions
THE Java virtual machine is designed to support the Java programminglanguage.
Oracle’s JDK software contains both a compiler from source code written inthe
Java programming language to the instruction set of the Java virtualmachine, and a
runtime system that implements the Java virtual machine itself.Understanding how
one compiler utilizes the Java virtual machine is useful to theprospective compiler
writer, as well as to one trying to understand the Java virtual machineitself. The
numbered sections in this chapter are not normative.
Note that the term “compiler” is sometimes used when referring to atranslator
from the instruction set of a Java virtual machine to the instruction setof a specific
CPU. One example of such a translator is a just-in-time (JIT) codegenerator,
which generates platform-specific instructions only after Java virtualmachine
code has been loaded. This chapter does not address issues associated withcode
generation, only those associated with compiling source code written inthe Java
programming languageto Java virtual machine instructions.
JVM被设计成支持java编程语言,Oracle JDK软件支持包含两套,一套被java语言编写的源代码编译成虚拟机指令集合的编译器。一套是JVM自己运行时环境。理解一个编译器如何应用在JVM中,对一个未来编译器工作者是非常有用的。这也是很好的理解JVM本身自己。
注意,提到的编译器有时是指从JVM指令转换到CPU特殊的机器指令,比如JIT代码生成器,用来生成平台特殊的指令当JVM代码被加载以后。这章不讨论这种代码生成器,只讨论被java程序语言编写的源代码转换为JVM指令。
1.1 Formatof Examples
This chapter consists mainly of examples of source code together withannotated
listings of the Java virtual machine code that the javac compiler in Oracle’sJDK
release 1.0.2 generates for the examples. The Java virtual machine code iswritten in
the informal “virtual machine assembly language” output by Oracle’s javap utility,
distributed with the JDK release. You can use javap to generate additionalexamples
of compiled methods.
The format of the examples should be familiar to anyone who has readassembly
code. Each instruction takes the form
实例主要包括源代码和JVM代码注释列表,JVM代码注释列表是又oracle JDK 1.0.2版本来生成。你可以使用javap命令来生成编译方法的例子。
这个例子的格式对读取汇编代码的人都很熟悉,每个指令如下格式。
<index> <opcode> [<operand1> [<operand2>...]] [<comment>]
The <index>is the index of theopcode of the instruction in the array that contains
the bytes of Java virtual machine code for this method. Alternatively, the<index>
may be thought of as a byte offset from the beginning of the method. The <opcode>
is the mnemonic for the instruction’s opcode, and the zero or more <operandN>are
the operands of the instruction. The optional <comment> is given inend-of-line
comment syntax:
8 bipush 100 // Push int constant 100
Some of the material in the comments is emitted by javap; the rest is suppliedby
the authors. The <index> prefacing each instruction may be used asthe target of a
control transfer instruction. For instance, a goto 8 instruction transferscontrol to the
instruction at index 8. Note that the actual operands of Java virtualmachine control
transfer instructions are offsets from the addresses of the opcodes ofthose instructions;
these operands are displayed by javap (and are shown in this chapter) as
more easily read offsets into their methods.
We preface an operand representing a runtime constant pool index with a hash
sign and follow the instruction by a comment identifying the runtimeconstant
pool item referenced, as in
10 ldc #1 // Push float constant 100.0
or
9 invokevirtual #4 // Method Example.addTwo(II)I
For the purposes of this chapter, we do not worry about specifying detailssuch as
operand sizes.
1.2 Useof Constants, Local Variables, and Control Constructs
使用常量,局部变量和控制结构
Java virtual machine code exhibits a set of general characteristicsimposed by the
Java virtual machine’s design and use of types. In the first example weencounter
many of these, and we consider them in some detail.
JVM代码展示了一系列JVM设计和使用类型的通用特性,在第一个例子中我们遇到许多这样的特性。
The spin method simply spinsaround an empty for loop 100 times:
void spin() {
int i;
for (i = 0; i < 100; i++) {
; // Loop body is empty
}
}
A compiler might compile spin to
Method void spin()
0 iconst_0 // Push int constant 0
1 istore_1 // Store into local variable 1 (i=0)
2 goto 8 // First time through don’t increment
5 iinc 1 1 // Increment local variable 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
14 return // Return void when done
The Java virtual machine is stack-oriented, with most operations takingone or
more operands from the operand stack of the Java virtual machine’s currentframe
or pushing results back onto the operand stack. A new frame is createdeach time a
method is invoked, and with it is created a new operand stack and set oflocal variables
for use by that method (see Section 2.6, “Frames”). At any one point ofthe
computation, there are thus likely to be many frames and equally manyoperand
stacks per thread of control, corresponding to many nested methodinvocations.
Only the operand stack in the current frame is active.
JVM是栈结构,大部分的操作把一个或者多个操作数从一个栈放入到另外一个栈,或者把结果压入到操作数栈。一个新的栈帧在每个方法被调用时被创建。同时会创建操作数栈用于存放一系列为方法所使用的局部变量,在任何计算时刻,每个线程控制的操作数栈必须和栈帧相等,嵌套方法调用,仅仅只有当前帧是活跃的。
The instruction set of the Java virtual machine distinguishes operandtypes by
using distinct bytecodes for operations on its various data types. Themethod spin
operates only on values of type int. The instructions in its compiled codechosen
to operate on typed data (iconst_0, istore_1, iinc, iload_1, if_icmplt) are all specialized
for type int.
JVM指令集合根据数据类型使用单独的字节码来区分操作类型。
The two constants in spin, 0 and 100, are pushed onto the operand stack
using two different instructions. The 0 is pushed using an iconst_0 instruction, one
of the family of iconst_<i> instructions. The 100 is pushed using a bipush
instruction, which fetches the value it pushes as an immediate operand.
The Java virtual machine frequently takes advantage of the likelihood ofcertain
operands (int constants −1, 0, 1, 2, 3, 4 and 5 in the case of the iconst_<i>
instructions) by making those operands implicit in the opcode. Because the
iconst_0 instruction knows it is going to push an int 0, iconst_0 does not need to
store an operand to tell it what value to push, nor does it need to fetchor decode
an operand. Compiling the push of 0 as bipush 0 would have beencorrect, but
would have made the compiled code for spin one byte longer. Asimple virtual
machine would have also spent additional time fetching and decoding theexplicit
operand each time around the loop. Use of implicit operands makes compiled
code more compact and efficient.
The int i in spin is stored as Javavirtual machine local variable 1. Because
most Java virtual machine instructions operate on values popped from theoperand
stack rather than directly on local variables, instructions that transfervalues
between local variables and the operand stack are common in code compiledfor
the Java virtual machine. These operations also have special support inthe
instruction set. In spin, values are transferred to and from local variablesusing
the istore_1 and iload_1 instructions, each ofwhich implicitly operates on local
variable 1. The istore_1 instruction pops an int from the operand stackand stores
it in local variable 1. The iload_1 instruction pushes the value in local variable 1
onto the operand stack.
The use (and reuse) of local variables is the responsibility of thecompiler
writer. The specialized load and store instructions should encourage thecompiler
writer to reuse local variables as much as is feasible. The resulting codeis faster,
more compact, and uses less space in the frame.
Certain very frequent operations on local variables are catered tospecially by
the Java virtual machine. The iinc instruction increments the contents of a local
variable by a one-byte signed value. The iinc instruction in spin increments the
first local variable (its first operand) by 1 (its second operand). The iinc instruction
is very handy when implementing looping constructs.
The for loop of spin is accomplished mainlyby these instructions:
5 iinc 1 1 // Increment local 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
The bipush instruction pushes thevalue 100 onto the operand stackas an int, then
the if_icmplt instruction pops thatvalue off the operand stack and compares it
against i. If the comparison succeeds (the variable i is less than 100), control is
transferred to index 5 and the next iteration of the for loop begins.Otherwise, control
passes to the instruction following the if_icmplt.
If the spin example had used adata type other than int for the loop counter,
the compiled code would necessarily change to reflect the different datatype. For
instance, if instead of an int the spin example uses a double, as shown,
void dspin() {
double i;
for (i = 0.0; i < 100.0; i++) {
; // Loop body is empty
}
}
the compiled code is
Method void dspin()
0 dconst_0 // Push double constant 0.0
1 dstore_1 // Store into local variables 1 and 2
2 goto 9 // First time through don’t increment
5 dload_1 // Push local variables 1 and 2
6 dconst_1 // Push double constant 1.0
7 dadd // Add; there is no dinc instruction
8 dstore_1 // Store result in local variables 1 and 2
9 dload_1 // Push local variables 1 and 2
10 ldc2_w #4 // Push double constant 100.0
13 dcmpg // There is no if_dcmplt instruction
14 iflt 5 // Compare and loop if less than (i < 100.0)
17 return // Return void when done
The instructions that operate on typed data are now specialized for type double.
(The ldc2_w instruction will bediscussed later in this chapter.)
Recall that double values occupy two local variables, although they are only
accessed using the lesser index of the two local variables. This is alsothe case for
values of type long. Again for example,
double值需要两个局部变量,只能访问低位索引来访问两个局部变量,long类型也一样。
double doubleLocals(double d1, double d2) {
return d1 + d2;
}
becomes
Method double doubleLocals(double,double)
0 dload_1 // First argument in local variables 1 and 2
1 dload_3 // Second argument in local variables 3 and 4
2 dadd
3 dreturn
Note that local variables of the local variable pairs used to store double values
in doubleLocals must never bemanipulated individually.
The Java virtual machine’s opcode size of 1 byte results in its compiledcode
being very compact. However, 1-byte opcodes also mean that the Javavirtual
machine instruction set must stay small. As a compromise, the Java virtual
machine does not provide equal support for all data types: it is notcompletely
orthogonal (see Table 2.2, “Type support in the Java virtual machineinstruction
set”).
For example, the comparison of values of type int in the for statement of
example spin can be implementedusing a single if_icmplt instruction; however,
there is no single instruction in the Java virtual machine instruction setthat performs
a conditional branch on values of type double. Thus, dspin must implement
its comparison of values of type double using a dcmpg instruction followed
by an iflt instruction.
The Java virtual machine provides the most direct support for data of type
int. This is partly in anticipation of efficientimplementations of the Java virtual
machine’s operand stacks and local variable arrays. It is also motivatedby the frequency
of int data in typicalprograms. Other integral types have less direct support.
There are no byte, char, or short versions of the store, load, or add
instructions, for instance. Here is the spin example written usinga short:
void sspin() {
short i;
for (i = 0; i < 100; i++) {
; // Loop body is empty
}
}
It must be compiled for the Java virtual machine, as follows, usinginstructions
operating on another type, most likely int, converting between short and int
values as necessary to ensure that the results of operations on short data stay
within the appropriate range:
Method void sspin()
0 iconst_0
1 istore_1
2 goto 10
5 iload_1 // The short is treatedas though an int
6 iconst_1
7 iadd
8 i2s // Truncate int to short
9 istore_1
10 iload_1
11 bipush 100
13 if_icmplt 5
16 return
The lack of direct support for byte, char, and short types in the Javavirtual
machine is not particularly painful, because values of those types areinternally promoted
to int (byte and short are sign-extended to int, char is zero-extended).
Operations on byte, char, and short data can thus be done using int instructions.
The only additional cost is that of truncating the values of int operations to valid
ranges.
The long and floating-pointtypes have an intermediate level of support in the
Java virtual machine, lacking only the full complement of conditionalcontrol
transfer instructions.
1.3 Arithmetic
运算
The Java virtual machine generally does arithmetic on its operand stack.(The
exception is the iinc instruction, which directly increments the value of alocal variable.)
For instance, the align2grain method aligns an int value to a given power
JVM一般通过操作数栈来运算。
of 2:
int align2grain(int i, int grain) {
return ((i + grain-1) & ~(grain-1));
}
Operands for arithmetic operations are popped from the operand stack, and
the results of operations are pushed back onto the operand stack. Resultsof arithmetic
subcomputations can thus be made available as operands of their nesting
computation. For instance, the calculation of ~(grain−1) is handled by these
算数运算操作数是从操作数栈弹出操作数,然后把操作结果压入到操作数栈。
中间运算(arithmetic subcomputations)结果在他们的嵌套计算中可能被当做操作数使用
instructions:
5 iload_2 // Push grain
6 iconst_1 // Push int constant 1
7 isub // Subtract; push result
8 iconst_m1 // Push int constant −1
9 ixor // Do XOR; push result
First grain − 1 is calculated usingthe contents of local variable 2 and an immediate
int value 1. These operands are popped from the operand stack andtheir
difference pushed back onto the operand stack. The difference is thusimmediately
available for use as one operand of the ixor instruction. (Recall that ∼x ==
−1^x.) Similarly, the result of the ixor instruction becomes anoperand for the
subsequent iand instruction.
The code for the entire method follows:
Method int align2grain(int,int)
0 iload_1
1 iload_2
2 iadd
3 iconst_1
4 isub
5 iload_2
6 iconst_1
7 isub
8 iconst_m1
9 ixor
10 iand
11 ireturn
1.4 Accessingthe Runtime Constant Pool
访问运行时常量池
Many numeric constants, as well as objects, fields, and methods, areaccessed via
the runtime constant pool of the current class. Object access isconsidered later
(§3.8). Data of types int, long, float, and double, as well as references to
instances of class String, are managed using the ldc, ldc_w, and ldc2_w instructions.
The ldc and ldc_w instructions are usedto access values in the runtime constant
pool (including instances of class String) of types other than double and
long. The ldc_w instruction is used in place of ldc only when there is a largenumber
of runtime constant pool items and a larger index is needed to access anitem.
The ldc2_w instruction is used toaccess all values of types double and long;
there is no non-wide variant.
许多数值常量,比如对象,字段,方法都是通过当前类的运行时常量池来访问。对象访问稍后讨论。数据类型int, long,float,double以及类String实例引用将由ldc,ldc_w,ldc2_w指令实现。
Ldc和ldc_w指令被用作访问运行时常量池对象包括类String对象,而不包括(other than)double和long。Ldc_w指令相对ldc被使用的地方在于当要访问常量池大量的数据项和大的索引需要被访问,ldc2_w指令用于访问double,long 类型所有的数据。
Integral constants of types byte, char, or short, as well as small int values,
may be compiled using the bipush, sipush, or iconst_<i> instructions, as seen earlier
(§3.2). Certain small floating-point constants may be compiled using the
fconst_<f> and dconst_<d> instructions.
In all of these cases, compilation is straightforward. For instance, theconstants
for
void useManyNumeric() {
int i = 100;
int j = 1000000;
long l1 = 1;
long l2 = 0xffffffff;
double d = 2.2;
...do some calculations...
}
are set up as follows:
Method void useManyNumeric()
0 bipush 100 // Push a small int with bipush
2 istore_1
3 ldc #1 // Push int constant 1000000; a larger int
// value uses ldc
5 istore_2
6 lconst_1 // A tiny long value usesshort, fast lconst_1
7 lstore_3
8 ldc2_w #6 // Push long0xffffffff (that is, anint −1); any
// long constant value can be pushed using ldc2_w
11 lstore 5
13 ldc2_w #8 // Push double constant 2.200000; uncommon
// double values are also pushed using ldc2_w
16 dstore 7
...do those calculations...
1.5 MoreControl Examples
更多的控制结构实例
Compilation of for statements was shown in an earlier section (§3.2). Mostof the Java
programming language’s other control constructs (if-then-else, do, while,
break, and continue) are also compiled in the obvious ways. The compilationof
switch statements is handled in a separate section (Section3.10, “Compiling
Switches”), as are the compilation of exceptions (Section 3.12, “Throwingand Handling
Exceptions”) and the compilation of finally clauses (Section 3.13,“Compiling
finally”).
As a further example, a while loop is compiled in an obvious way, although
the specific control transfer instructions made available by the Javavirtual
machine vary by data type. As usual, there is more support for data oftype int,
for example:
void whileInt() {
int i = 0;
while (i < 100) {
i++;
}
}
is compiled to
Method void whileInt()
0 iconst_0
1 istore_1
2 goto 8
5 iinc 1 1
8 iload_1
9 bipush 100
11 if_icmplt 5
14 return
Note that the test of the while statement (implemented using the if_icmplt
instruction) is at the bottom of the Java virtual machine code for theloop. (This
was also the case in the spin examples earlier.) The test being at the bottom of the
loop forces the use of a goto instruction to get to the test prior to the firstiteration
of the loop. If that test fails, and the loop body is never entered, thisextra instruction
is wasted. However, while loops are typically used when their body is
expected to be run, often for many iterations. For subsequent iterations,putting
the test at the bottom of the loop saves a Java virtual machineinstruction each
time around the loop: if the test were at the top of the loop, the loopbody would
need a trailing goto instruction to get back to the top.
Control constructs involving other data types are compiled in similarways, but
must use the instructions available for those data types. This leads tosomewhat less
efficient code because more Java virtual machine instructions are needed,for example:
注意while语句(使用if_icmplt指令实现)在JVM代码中测试条件是在底部。测试条件在循环底部使用goto指令强制转换到第一次迭代前执行。如果测试失败,则循环体从来不会进入,退出指令也就自然无效。尽管如此,循环通常期望自己的循环体被执行。
提供许多迭代器,在嵌套迭代器中,把测试放在循环体底部,这样相当于节省了一条JVM指令。如果测试条件放在顶部,那么循环体在最后需要一个goto指令来跳转到顶部。
控制指令调用其他的数据类型才用相同的方式编译,但是必须更加数据类型使用对应的指令。这样会因为JVM需要更多的指令而导致低效。
void whileDouble() {
double i = 0.0;
while (i < 100.1) {
i++;
}
}
is compiled to
Method void whileDouble()
0 dconst_0
1 dstore_1
2 goto 9
5 dload_1
6 dconst_1
7 dadd
8 dstore_1
9 dload_1
10 ldc2_w #4 // Push double constant 100.1
13 dcmpg // To do the compare and branch we have touse...
14 iflt 5 // ...two instructions
17 return
Each floating-point type has two comparison instructions: fcmpl and fcmpg
for type float, and dcmpl and dcmpg for type double. The variants differonly in
their treatment of NaN. NaN is unordered, so all floating-pointcomparisons fail if
either of their operands is NaN. The compiler chooses the variant of thecomparison
instruction for the appropriate type that produces the same result whetherthe
comparison fails on non-NaN values or encounters a NaN.
For instance:
int lessThan100(double d) {
if (d < 100.0) {
return 1;
} else {
return -1;
}
}
compiles to
Method int lessThan100(double)
0 dload_1
1 ldc2_w #4 // Push double constant 100.0
4 dcmpg // Push 1 if d is NaN or d > 100.0;
// push 0 if d == 100.0
5 ifge 10 // Branch on 0 or 1
8 iconst_1
9 ireturn
10 iconst_m1
11 ireturn
If d is not NaN and is lessthan 100.0, the dcmpg instruction pushes an int –1 onto
the operand stack, and the ifge instruction does not branch. Whether d is greater than
100.0 or is NaN, the dcmpg instruction pushes an int 1 onto the operandstack,
and the ifge branches. If d is equal to 100.0, the dcmpg instruction pushes an int 0
onto the operand stack, and the ifge branches.
The dcmpl instruction achievesthe same effect if the comparison is
reversed:
int greaterThan100(double d) {
if (d > 100.0) {
return 1;
} else {
return -1;
}
}
becomes
Method int greaterThan100(double)
0 dload_1
1 ldc2_w #4 // Push double constant 100.0
4 dcmpl // Push −1 if d is Nan or d < 100.0;
// push 0 if d == 100.0
5 ifle 10 // Branch on 0 or −1
8 iconst_1
9 ireturn
10 iconst_m1
11 ireturn
Once again, whether the comparison fails on a non-NaN value or because itis
passed a NaN, the dcmpl instruction pushes an int value onto the operandstack
that causes the ifle to branch. If both of the dcmp instructions did not exist, one of
the example methods would have had to do more work to detect NaN.
1.6 ReceivingArguments
接受参数
If n arguments are passedto an instance method, they are received, by convention, in
the local variables numbered 1 through n of the frame created for the new method
invocation. The arguments are received in the order they were passed. Forexample:
如果n个参数通过实例方法被传递。他们接受这个参数,按照惯例,新方法调用需要数目为n的局部变量。参数按照他们传递的顺序接受。
int addTwo(int i, int j) {
return i + j;
}
compiles to
Method int addTwo(int,int)
0 iload_1 // Push value of local variable 1 (i)
1 iload_2 // Push value of local variable 2 (j)
2 iadd // Add; leave int result onoperand stack
3 ireturn // Return int result
By convention, an instance method is passed a reference to its instance in
local variable 0. In the Java programming language the instance isaccessible via
the this keyword.
Class (static) methods do not havean instance, so for them this use of local
variable zero is unnecessary. A class method starts using local variablesat index
zero. If the addTwo method were a class method, its arguments would be passedin
a similar way to the first version:
一个实例方法传递这个实例的引用给第0个局部变量。在java程序语言中,这个实例通过this关键字访问。
类的静态方法,没有实例,所以使用第0个局部变量保存关键字this是不必须的。类方法开始使用局部变量索引从0开始,如果addTwo方法是类方法,那么他的参数传递和第一个版本类似。
static int addTwoStatic(int i, int j) {
return i + j;
}
compiles to
Method int addTwoStatic(int,int)
0 iload_0
1 iload_1
2 iadd
3 ireturn
The only difference is that the method arguments appear starting in localvariable 0
rather than 1.
这里仅有不同的地方是方法参数是从0开始而不是从1开始。
1.7 InvokingMethods
方法调用
The normal method invocation for a instance method dispatches on theruntime type
of the object. (They are virtual, in C++ terms.) Such an invocation isimplemented
using the invokevirtual instruction, which takes as its argument anindex to a runtime
constant pool entry giving the internal form of the binary name of theclass type
of the object, the name of the method to invoke, and that method’sdescriptor
(§4.3.3). To invoke the addTwo method, defined earlier as an instance method, we
might write
普通的实例方法调用是在运行时根据对象类型分派。这种调用使用invokevirtual指令,
每个指令有一个带索引的参数,运行时常量池从这个索引中获取方法的符号引用,这个符号引用提供对象类类型的名称的二级制,调用方法的名称,以及方法的描述信息
int add12and13() {
return addTwo(12, 13);
}
This compiles to
Method int add12and13()
0 aload_0 // Push local variable 0 (this)
1 bipush 12 // Push int constant 12
3 bipush 13 // Push int constant 13
5 invokevirtual #4 // Method Example.addtwo(II)I
8 ireturn // Return int on top ofoperand stack; it is
// the int result of addTwo()
The invocation is set up by first pushing a reference to the currentinstance,
this, onto the operand stack. The method invocation’sarguments, int values 12
and 13, are then pushed.When the frame for the addTwo method is created, the
arguments passed to the method become the initial values of the newframe’s local
variables. That is, the reference for this and the two arguments, pushed onto
the operand stack by the invoker, will become the initial values of localvariables
0, 1, and 2 of the invoked method.
Finally, addTwo is invoked. When itreturns, its int return value is pushed
onto the operand stack of the frame of the invoker, the add12and13 method. The
return value is thus put in place to be immediately returned to theinvoker of
add12and13.
这个调用首先把当前对象的引用给压入到操作数栈,方法调用的参数,整数类型的12和13随后压入。当addTwo方法的栈帧被创建的时候,参数通过参数传递到方法边成栈帧局部变量的初始值。至此,引用this以及两个参数都被调用者压入到操作数栈,变成调用方法局部变量1,2,3的初始值。
最后addTwo方法被调用,当返回的时候,返回的整数值被调用者压入到栈帧操作数栈,返回的值立刻被放到调用add12and13方法调用的地方。
The return from add12and13 is handled by the ireturn instruction of
add12and13. The ireturn instruction takes the int value returned by addTwo, on
the operand stack of the current frame, and pushes it onto the operandstack of the
frame of the invoker. It then returns control to the invoker, making theinvoker’s
frame current. The Java virtual machine provides distinct returninstructions for
many of its numeric and reference data types, as well as a return instruction for
methods with no return value. The same set of return instructions is usedfor all
varieties of method invocations.
从add12and13返回通过ireturn指令处理。Ireturn指令把addTwo返回的int值。又当前栈帧的操作数栈中压入到栈帧调用者操作数栈中。然后控制交给调用者,使用调用的真栈帧为当前栈帧.JVM 为许多数值和引用类型提供独立的返回指令。
The operand of the invokevirtual instruction (in the example, the runtimeconstant
pool index #4) is not the offset of the method in the class instance.The compiler
does not know the internal layout of a class instance. Instead, itgenerates
symbolic references to the methods of an instance, which are stored in theruntime
constant pool. Those runtime constant pool items are resolved at runtimeto determine
the actual method location. The same is true for all other Java virtualmachine
instructions that access class instances.
Invoking addTwoStatic, a class (static) variant of addTwo, is similar, as
shown:
这个invokevirtual指令操作不是类实例方法的偏移量。编译器不知道实例类的结构,而是,它为实例创建方法符号应用,并且存储在运行时常量池中,这些运行时常量池会再运行时换成方法的时间调用地址。这种方式对JVM其它指令也是如此。
int add12and13() {
return addTwoStatic(12, 13);
}
although a different Java virtual machine method invocation instruction isused:
Method int add12and13()
0 bipush 12
2 bipush 13
4 invokestatic #3 // Method Example.addTwoStatic(II)I
7 ireturn
Compiling an invocation of a class (static) method is very muchlike compiling an
invocation of an instance method, except this is not passed by theinvoker. The
method arguments will thus be received beginning with local variable 0 (see Section
3.6, “Receiving Arguments”). The invokestatic instruction is alwaysused to invoke
class methods.
The invokespecialinstruction must beused to invoke instance initialization
methods (see Section 3.8, “Working with Class Instances”). It is also used
when invoking methods in the superclass (super) and when invoking private
methods. For instance, given classes Near and Far declared as
编译类的静态方法和编译实例方法类似,区别在于没有传递this通过调用者。方法参数接受从0开始。Invokestatic 指令总是用来调用类方法。
invokespecial指令必须只能调用实例的初始化方法。也用于调用超类方法和私有方法。
class Near {
int it;
public int getItNear() {
return getIt();
}
private int getIt() {
return it;
}
}
class Far extends Near {
int getItFar() {
return super.getItNear();
}
}
the method Near.getItNear (which invokes a private method) becomes
Method int getItNear()
0 aload_0
1 invokespecial #5 // Method Near.getIt()I
4 ireturn
The method Far.getItFar (which invokes a superclass method) becomes
Method int getItFar()
0 aload_0
1 invokespecial #4 // Method Near.getItNear()I
4 ireturn
Note that methods called using the invokespecial instruction alwayspass this to
the invoked method as its first argument. As usual, it is received inlocal variable 0.
To invoke the target of a method handle, a compiler must form a method
descriptor that records the actual argument and return types. A compilermay not
perform method invocation conversions on the arguments; instead, it mustpush
them on the stack according to their own unconverted types. The compiler
arranges for a reference to the method handle object to be pushed on thestack
before the arguments, as usual. The compiler emits an invokevirtualinstruction
that references a descriptor which describes the argument and returntypes. By
special arrangement with method resolution (§5.4.3.3), an invokevirtualinstruction
which invokes the invokeExact or invoke methods of
java.lang.invoke.MethodHandle will always link, provided the method
descriptor is syntactically well-formed and the types named in thedescriptor can
be resolved.
注意到方法使用invokespecial指令总是把this当做第一个参数传递给调用方法。一般接受放在第0个局部变量中给调用的目标方法使用。编译器必须知道方法的表述信息才能记录实际的参数和返回类型。编译器许多时候不执行方法调用参数的转换,而是按照自己的非转换类型把他们放入栈。在参数处理之前,编译器安排方法处理对象的引用放入栈,编译器在生成invokevirtual指令时,同时关联上引用的描述包括参数描述和返回类型。作为方法解决的特殊安排。invokeExact调用的Invokevirtual指令或者java.lang.invoke.MethodHandle的invoke方法提供方法描述。
1.8 Workingwith Class Instances
Java virtual machine class instances are created using the Java virtualmachine’s
new instruction. Recall that at the level of the Java virtualmachine, a constructor
appears as a method with the compiler-supplied name <init>. This specially
named method is known as the instance initialization method (§2.9).Multiple
instance initialization methods, corresponding to multiple constructors,may exist
for a given class. Once the class instance has been created and itsinstance variables,
including those of the class and all of its superclasses, have beeninitialized to their
default values, an instance initialization method of the new classinstance is
invoked. For example:
JVM类实例创建一般使用JVM的new指令。在JVM这一级别被调用。构造函数会以一个编译器命名的函数<init>方法出现。这个特殊的方法就是总所周知的实例初始化方法。多个实例初始化方法对应的有多个构造函,一旦实例被创建,那么它的变量包含类及所有的父类都会被初始化为他们默认的值。然后类实例的初始化方法将被调用。
Object create() {
return new Object();
}
compiles to
Method java.lang.Object create()
0 new #1 // Class java.lang.Object
3 dup
4 invokespecial #4 // Method java.lang.Object.<init>()V
7 areturn
Class instances are passed and returned (as reference types) very much like
numeric values, although type reference has its own complement of instructions,
for example:
int i; // An instance variable
MyObj example() {
MyObj o = new MyObj();
return silly(o);
}
MyObj silly(MyObj o) {
if (o != null) {
return o;
} else {
return o;
}
}
becomes
Method MyObj example()
0 new #2 // Class MyObj
3 dup
4 invokespecial #5 // Method MyObj.<init>()V
7 astore_1
8 aload_0
9 aload_1
10 invokevirtual #4
// Method Example.silly(LMyObj;)LMyObj;
13 areturn
Method MyObj silly(MyObj)
0 aload_1
1 ifnull 6
4 aload_1
5 areturn
6 aload_1
7 areturn
The fields of a class instance (instance variables) are accessed using thegetfield
and putfield instructions. If i is an instancevariable of type int, the methods
setIt and getIt, defined as
类实例的字段通过getfield和putfield指令方法,如果i是实例的int类型变量,这个方法有setlt和getlt 定义如下:
void setIt(int value) {
i = value;
}
int getIt() {
return i;
}
become
Method void setIt(int)
0 aload_0
1 iload_1
2 putfield #4 // Field Example.i I
5 return
Method int getIt()
0 aload_0
1 getfield #4 // Field Example.i I
4 ireturn
As with the operands of method invocation instructions, the operands ofthe putfield
and getfield instructions (theruntime constant pool index #4) are not the offsets of
the fields in the class instance. The compiler generates symbolicreferences to the
fields of an instance, which are stored in the runtime constant pool.Those runtime
constant pool items are resolved at runtime to determine the location ofthe field
within the referenced object.
对于方法调用指令或者putfied和getfield指令,都不是实例字段的偏移量。而是编译器生成了一个引用符号位实例的字段。存储在运行时常量池中。运行时常量池在运行时会解释成字段引用的实际位置。
1.9 Arrays
Java virtual machine arrays are also objects. Arrays are created andmanipulated
using a distinct set of instructions. The newarray instruction is used tocreate an
array of a numeric type. The code
JVM数组也是对象,创建数组使用单独的指令集合。newarray指令用于创建数值类型的数组。
void createBuffer() {
int buffer[];
int bufsz = 100;
int value = 12;
buffer = new int[bufsz];
buffer[10] = value;
value = buffer[11];
}
might be compiled to
Method void createBuffer()
0 bipush 100 // Push int constant 100(bufsz)
2 istore_2 // Store bufsz in localvariable 2
3 bipush 12 // Push int constant 12(value)
5 istore_3 // Store value in localvariable 3
6 iload_2 // Push bufsz...
7 newarray int // ...and create new array of int of thatlength
9 astore_1 // Store new array in buffer
10 aload_1 // Push buffer
11 bipush 10 // Push int constant 10
13 iload_3 // Push value
14 iastore // Store value at buffer[10]
15 aload_1 // Push buffer
16 bipush 11 // Push int constant 11
18 iaload // Push value at buffer[11]...
19 istore_3 // ...and store it in value
20 return
The anewarray instruction is used tocreate a one-dimensional array of object
references, for example:
void createThreadArray() {
Thread threads[];
int count = 10;
threads = new Thread[count];
threads[0] = new Thread();
}
becomes
Method void createThreadArray()
0 bipush 10 // Push int constant 10
2 istore_2 // Initialize count to that
3 iload_2 // Push count, used byanewarray
4 anewarray class #1 // Create new array of class Thread
7 astore_1 // Store new array in threads
8 aload_1 // Push value of threads
9 iconst_0 // Push int constant 0
10 new #1 // Create instance of class Thread
COMPILING 62 FOR THE JAVA VIRTUAL MACHINE
13 dup // Make duplicate reference...
14 invokespecial #5 // ...to pass to instanceinitialization method
// Method java.lang.Thread.<init>()V
17 aastore // Store new Thread in array at 0
18 return
The anewarray instruction can alsobe used to create the first dimension of a multidimensional
array. Alternatively, the multianewarray instruction can be used to create
several dimensions at once. For example, the three-dimensional array:
anewarray指令创建多维数组的第一纬维,但是multianewaary指令能够一次创建维多为数组。
int[][][] create3DArray() {
int grid[][][];
grid = new int[10][5][];
return grid;
}
is created by
Method int create3DArray()[][][]
0 bipush 10 // Push int 10 (dimensionone)
2 iconst_5 // Push int 5 (dimensiontwo)
3 multianewarray #1 dim #2 // Class [[[I, a three
// dimensional int array;
// only create first two
// dimensions
7 astore_1 // Store new array...
8 aload_1 // ...then prepare to return it
9 areturn
The first operand of the multianewarray instruction is the runtime constant pool
index to the array class type to be created. The second is the number ofdimensions
of that array type to actually create. The multianewarray instruction can beused to
create all the dimensions of the type, as the code for create3DArray shows. Note
that the multidimensional array is just an object and so is loaded andreturned by an
aload_1 and areturn instruction, respectively. For information about arrayclass
names, see Section 4.4.1.
All arrays have associated lengths, which are accessed via the arraylength
instruction.
Multianewarray指令的第一个操作是在运行时常量池索引,它表示将要被创建数组类型。第二个操作是数组维度的数量。Multianewarray指令被用作创建所有类型维度的数组,注意,多维数组仅仅是一个对象并且被加载,然后通过aload_l加载和areturn指令返回。
所有的数组关联的长度,被arraylength指令访问。
1.10 CompilingSwitches
Compilation of switch statements uses the tableswitch and lookupswitchinstructions.
The tableswitch instruction is usedwhen the cases of the switch can be efficiently
represented as indices into a table of target offsets. The default target of the
switch is used if the value of the expression of the switch falls outside therange
of valid indices. For instance,
编译switch语句使用tableswitch和lookupswitch指令,tableswitch指令被用在switch能够表示的目标对象有效偏移量。Switch的default对象被用在如果switch表达式的值落在索引表示的范围以外。
int chooseNear(int i) {
switch (i) {
case 0: return 0;
case 1: return 1;
case 2: return 2;
default: return -1;
}
}
compiles to
Method int chooseNear(int)
0 iload_1 // Push local variable 1 (argument i)
1 tableswitch 0 to 2: // Valid indices are 0 through 2
0: 28 // If i is 0, continueat 28
1: 30 // If i is 1, continueat 30
2: 32 // If i is 2, continueat 32
default:34 // Otherwise, continue at 34
28 iconst_0 // i was 0; push int constant 0...
29 ireturn // ...and return it
30 iconst_1 // i was 1; push int constant 1...
31 ireturn // ...and return it
32 iconst_2 // i was 2; push int constant 2...
33 ireturn // ...and return it
34 iconst_m1 // otherwise push int constant –1...
35 ireturn // ...and return it
The Java virtual machine’s tableswitch and lookupswitch instructions operate
only on int data. Becauseoperations on byte, char, or short values are
internally promoted to int, a switch whose expression evaluates to one of
those types is compiled as though it evaluated to type int. If the chooseNear
method had been written using type short, the same Javavirtual machine
instructions would have been generated as when using type int. Other numeric
types must be narrowed to type int for use in a switch.
Where the cases of the switch are sparse, the table representation of the
tableswitch instruction becomes inefficient in terms of space. The lookupswitch
instruction may be used instead. The lookupswitch instruction pairs int keys
(the values of the case labels) with target offsets in a table. When a lookupswitch
instruction is executed, the value of the expression of the switch is
compared against the keys in the table. If one of the keys matches thevalue of
the expression, execution continues at the associated target offset. If nokey
matches, execution continues at the default target. For instance,the compiled
JVM的tableswitch和lookupswitch指令只操作int类型数据,因为操作byte,short,char类型的值都会转换为int.switch以上几种类型表达式的值会被编译成int。如果chooseNear方法使用short类型作为条件。JVM同样会把它当做int类型生成。如果是其它数值类型的值同样会被转换为int。
当switch条件比较稀疏的时候。Tableswitch指令变得比较低效。而lookupswitch可以替代它,lookupswitch指令是一对int关键字和索引表偏移量构成。当lookupswitch指令被执行的时候条件表达式值和索引表中的关键字值进行比较,如果其中一个关键字值匹配上了表达式值,继续执行对应关联的偏移量。如果匹配不上,继续执行default分支。
code for
int chooseFar(int i) {
switch (i) {
case -100: return -1;
case 0: return 0;
case 100: return 1;
default: return -1;
}
}
looks just like the code for chooseNear, except for the lookupswitchinstruction:
Method int chooseFar(int)
0 iload_1
1 lookupswitch 3:
−100: 36
0: 38
100: 40
default:42
36 iconst_m1
37 ireturn
38 iconst_0
39 ireturn
40 iconst_1
41 ireturn
42 iconst_m1
43 ireturn
The Java virtual machine specifies that the table of the lookupswitchinstruction
must be sorted by key so that implementations may use searches moreefficient
than a linear scan. Even so, the lookupswitch instruction mustsearch its keys
for a match rather than simply perform a bounds check and index into atable like
tableswitch. Thus, a tableswitch instruction is probably more efficient than a lookupswitch
where space considerations permit a choice.
JVM规范是lookupswitch指令额索引表必须是按关键字排序。
1.11 Operationson the Operand Stack
The Java virtual machine has a large complement of instructions thatmanipulate the
contents of the operand stack as untyped values. These are useful becauseof the
Java virtual machine’s reliance on deft manipulation of its operand stack.For
instance,
JVM有许多指令组件并且不分数据类型,这是非常有用的,因为JVM是基于操作数栈。
public long nextIndex() {
return index++;
}
private long index = 0;
is compiled to
Method long nextIndex()
0 aload_0 // Push this
1 dup // Make a copy of it
2 getfield #4 // One of the copies of this is consumed
// pushing long field index,
// above the original this
5 dup2_x1 // The long on top ofthe operand stack is
// inserted into the operand stack below the
// original this
6 lconst_1 // Push long constant 1
7 ladd // The index value is incremented...
8 putfield #4 // ...and the result stored back in thefield
11 lreturn // The original value of index is left on
// top of the operand stack, ready to be returned
Note that the Java virtual machine never allows its operand stackmanipulation
instructions to modify or break up individual values on the operand stack.
注意到,JVM从来不允许操作数栈指令在操作数栈中单独修改或者拆分其值。
1.12 Throwingand Handling Exceptions
Exceptions are thrown from programs using the throw keyword. Itscompilation is
simple:
void cantBeZero(int i) throws TestExc {
if (i == 0) {
throw new TestExc();
}
}
becomes
Method void cantBeZero(int)
0 iload_1 // Push argument 1 (i)
1 ifne 12 // If i==0, allocateinstance and throw
4 new #1 // Create instance of TestExc
7 dup // One reference goes to the constructor
8 invokespecial #7 // Method TestExc.<init>()V
11 athrow // Second reference is thrown
12 return // Never get here if we threw TestExc
Compilation of try-catch constructs is straightforward. For example,
void catchOne() {
try {
tryItOut();
} catch (TestExc e) {
handleExc(e);
}
}
is compiled as
Method void catchOne()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 return // End of try block;normal return
5 astore_1 // Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #5 // Invoke handler method:
// Example.handleExc(LTestExc;)V
11 return // Return after handling TestExc
Exception table:
From To Target Type
0 4 5 Class TestExc
Looking more closely, the try block is compiled just as it would be if the try were
not present:
Method void catchOne()
0 aload_0 // Beginning of try block
1 invokevirtual #4 // Method Example.tryItOut()V
4 return // End of try block; normalreturn
If no exception is thrown during the execution of the try block, it behaves as
though the try were not there: tryItOut is invoked and catchOne returns.
Following the try block is the Java virtual machine code that implementsthe
single catch clause:
5 astore_1 // Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #5 // Invoke handler method:
// Example.handleExc(LTestExc;)V
11 return // Return after handling TestExc
Exception table:
From To Target Type
0 4 5 Class TestExc
The invocation of handleExc, the contents of the catch clause, is alsocompiled
like a normal method invocation. However, the presence of a catch clause causes
the compiler to generate an exception table entry (§2.10, §4.7.3). Theexception
table for the catchOne method has one entry corresponding to the one argument(an
instance of class TestExc) that the catch clause of catchOne can handle. If some
value that is an instance of TestExc is thrown during execution of theinstructions
between in-dices 0 and 4 in catchOne, control is transferred to the Java virtual
machine code at index 5, which implements the block of the catch clause. If the
value that is thrown is not an instance of TestExc, the catch clause of catchOne
cannot handle it. Instead, the value is rethrown to the invoker of catchOne.
A try may have multiple catch clauses:
void catchTwo() {
try {
tryItOut();
} catch (TestExc1 e) {
handleExc(e);
} catch (TestExc2 e) {
handleExc(e);
}
}
Multiple catch clauses of a given try statement are compiledby simply appending
the Java virtual machine code for each catch clause one after theother and adding
entries to the exception table, as shown:
Method void catchTwo()
0 aload_0 // Begin try block
1 invokevirtual #5 // Method Example.tryItOut()V
4 return // End of try block;normal return
5 astore_1 // Beginning of handler for TestExc1;
// Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #7 // Invoke handler method:
// Example.handleExc(LTestExc1;)V
11 return // Return after handling TestExc1
12 astore_1 // Beginning of handler for TestExc2;
// Store thrown value in local variable 1
13 aload_0 // Push this
14 aload_1 // Push thrown value
15 invokevirtual #7 // Invoke handler method:
// Example.handleExc(LTestExc2;)V
18 return // Return after handling TestExc2
Exception table:
From To Target Type
0 4 5 Class TestExc1
0 4 12 Class TestExc2
If during the execution of the try clause (between indices 0 and 4) a value is thrown
that matches the parameter of one or more of the catch clauses (the value isan
instance of one or more of the parameters), the first (innermost) such catch clause
is selected. Control is transferred to the Java virtual machine code forthe block of
that catch clause. If the valuethrown does not match the parameter of any of the
catch clauses of catchTwo, the Java virtual machine rethrows the value without
invoking code in any catch clause of catchTwo.
Nested try-catch statements are compiledvery much like a try statement
with multiple catch clauses:
void nestedCatch() {
try {
try {
tryItOut();
} catch (TestExc1 e) {
handleExc1(e);
}
} catch (TestExc2 e) {
handleExc2(e);
}
}
becomes
Method void nestedCatch()
0 aload_0 // Begin try block
1 invokevirtual #8 // Method Example.tryItOut()V
4 return // End of try block;normal return
5 astore_1 // Beginning of handler for TestExc1;
// Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #7 // Invoke handler method:
// Example.handleExc1(LTestExc1;)V
11 return // Return after handling TestExc1
12 astore_1 // Beginning of handler for TestExc2;
COMPILING 70 FOR THE JAVA VIRTUAL MACHINE
// Store thrown value in local variable 1
13 aload_0 // Push this
14 aload_1 // Push thrown value
15 invokevirtual #6 // Invoke handler method:
// Example.handleExc2(LTestExc2;)V
18 return // Return after handling TestExc2
Exception table:
From To Target Type
0 4 5 Class TestExc1
0 12 12 Class TestExc2
The nesting of catch clauses is represented only in the exception table. The
Java virtual machine does not enforce nesting of or any ordering of theexception
table entries (§2.10). However, because try-catch constructs arestructured, a compiler
can always order the entries of the exception handler table such that, forany
thrown exception and any program counter value in that method, the firstexception
handler that matches the thrown exception corresponds to the innermostmatching
catch clause.
For instance, if the invocation of tryItOut (at index 1) threw an instance of
TestExc1, it would be handled by the catch clause that invokes handleExc1.
This is so even though the exception occurs within the bounds of the outercatch
clause (catching TestExc2) and even though that outer catch clause might otherwise
have been able to handle the thrown value.
As a subtle point, note that the range of a catch clause is inclusive onthe
“from” end and exclusive on the “to” end (§4.7.3). Thus, the exceptiontable entry
for the catch clause catching TestExc1 does not cover the return instruction at
offset 4. However, the exception table entry for the catch clause catching
TestExc2 does cover the return instruction at offset 11. Return instructionswithin
nested catch clauses are includedin the range of instructions covered by nesting
catch clauses.
1.13 Compilingfinally
(This section assumes a compiler generates class files with versionnumber 50.0
or below, so that the jsr instruction may be used. See also Section 4.10.2.5,
“Exceptions and finally”.)
Compilation of a try-finally statement is similar to that of try-catch. Prior to
transferring control outside the try statement, whether that transfer is normalor
abrupt, because an exception has been thrown, the finally clause must first be
executed. For this simple example
void tryFinally() {
try {
tryItOut();
} finally {
wrapItUp();
}
}
the compiled code is
Method void tryFinally()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 jsr 14 // Call finally block
7 return // End of try block
8 astore_1 // Beginning of handler for any throw
9 jsr 14 // Call finally block
12 aload_1 // Push thrown value
13 athrow // ...and rethrow the value to the invoker
14 astore_2 // Beginning of finally block
15 aload_0 // Push this
16 invokevirtual #5 // Method Example.wrapItUp()V
19 ret 2 // Return from finally block
Exception table:
From To Target Type
0 4 8 any
There are four ways for control to pass outside of the try statement: by falling
through the bottom of that block, by returning, by executing a break or continue
statement, or by raising an exception. If tryItOut returns withoutraising an exception,
control is transferred to the finally block using a jsr instruction. The jsr 14
instruction at index 4 makes a “subroutine call” to the code for the finally block at
index 14 (the finally block is compiled asan embedded subroutine). When the
finally block completes, the ret 2 instruction returnscontrol to the instruction following
the jsr instruction at index 4.
In more detail, the subroutine call works as follows: The jsr instruction pushes
the address of the following instruction (return at index 7) onto the operandstack
before jumping. The astore_2 instruction that is the jump target stores the address
on the operand stack into local variable 2. The code for the finally block (in this
case the aload_0 and invokevirtual instructions) is run. Assuming execution of
that code completes normally, the ret instruction retrieves the address fromlocal
variable 2 and resumes execution at that address. The return instruction isexecuted,
and tryFinally returns normally.
A try statement with a finally clause is compiled tohave a special exception
handler, one that can handle any exception thrown within the try statement.
If tryItOut throws an exception,the exception table for tryFinally is searched
for an appropriate exception handler. The special handler is found,causing execution
to continue at index 8. The astore_1 instruction at index 8 stores the thrown
value into local variable 1. The following jsr instruction does a subroutine call to
the code for the finally block. Assuming that code returns normally, the
aload_1 instruction at index 12 pushes the thrown value back onto theoperand
stack, and the following athrow instruction rethrows the value.
Compiling a try statement with both a catch clause and a finally clause is
more complex:
void tryCatchFinally() {
try {
tryItOut();
} catch (TestExc e) {
handleExc(e);
} finally {
wrapItUp();
}
}
becomes
Method void tryCatchFinally()
0 aload_0 // Beginning of try block
1 invokevirtual #4 // Method Example.tryItOut()V
4 goto 16 // Jump to finally block
7 astore_3 // Beginning of handler for TestExc;
// Store thrown value in local variable 3
8 aload_0 // Push this
9 aload_3 // Push thrown value
10 invokevirtual #6 // Invoke handler method:
// Example.handleExc(LTestExc;)V
13 goto 16 // Huh???1
16 jsr 26 // Call finally block
19 return // Return after handling TestExc
20 astore_1 // Beginning of handler for exceptions
// other than TestExc, orexceptions
// thrown while handling TestExc
21 jsr 26 // Call finally block
24 aload_1 // Push thrown value...
25 athrow // ...and rethrow the value to the invoker
26 astore_2 // Beginning of finally block
27 aload_0 // Push this
28 invokevirtual #5 // Method Example.wrapItUp()V
31 ret 2 // Return from finally block
Exception table:
From To Target Type
0 4 7 Class TestExc
0 16 20 any
If the try statement completesnormally, the goto instruction at index 4 jumps
to the subroutine call for the finally block at index 16. The finally block at
index 26 is executed, controlreturns to the return instruction at index 19, and
tryCatchFinally returns normally.
If tryItOut throws an instance of TestExc, the first(innermost) applicable
exception handler in the exception table is chosen to handle theexception. The
1 This goto instruction isstrictly unnecessary, but is generated by the javac compiler of Oracle’s
JDK release 1.0.2.
code for that exception handler, beginning at index 7, passes the thrownvalue to
handleExc and on its return makes the same subroutine call to the finally block
at index 26 as in the normal case. If an exception is not thrown by handleExc,
tryCatchFinally returns normally.
If tryItOut throws a value that isnot an instance of TestExc or if handle-
Exc itself throws an exception, the condition is handled bythe second entry in the
exception table, which handles any value thrown between indices 0 and 16. That
exception handler transfers control to index 20, where the thrown value is first
stored in local variable 1. The code for the finally block at index 26 is called as
a subroutine. If it returns, the thrown value is retrieved from localvariable 1 and
rethrown using the athrow instruction. If a new value is thrown during execution
of the finally clause, the finally clause aborts, and tryCatchFinallyreturns
abruptly, throwing the new value to its invoker.
1.14 Synchronization
Synchronization in the Java virtual machine is implemented by monitorentry and
exit, either explicitly (by use of the monitorenter and monitorexit instructions) or
implicitly (by the method invocation and return instructions).
For code written in the Java programming language, perhaps the most common
form of synchronization is the synchronized method. A synchronized
method is not normally implemented using monitorenter and monitorexit. Rather,
it is simply distinguished in the runtime constant pool by the ACC_SYNCHRONIZED
flag, which is checked by the method invocation instructions (see Section2.11.10,
“Synchronization”).
同步在JVM中是基于管程的进入和退出,或者隐式或者显示。在java编程语言中,也许大部分的同步形式都是使用synchronized方法。一个synchronized方法不能正常实现使用monitorenter和monitorexit.而是简单的在运行时常量池使用单独ACC_SYNCHRONIZED标志。这个标志在方法指令调用时别检查到。
The monitorenterand monitorexit instructions enablethe compilation of
synchronized statements. For example:
Monitorenter和monitorexit指令用于实现同步代码块。
void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}
is compiled to
Method void onlyMe(Foo)
0 aload_1 // Push f
1 dup // Duplicate it on the stack
2 astore_2 // Store duplicate in local variable 2
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual #5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 goto 18 // Complete the method normally
13 astore_3 // In case of any throw, end up here
14 aload_2 // Push local variable 2 (f)
15 monitorexit // Be sure to exit the monitor!
16 aload_3 // Push thrown exception...
17 athrow // ...then rethrow the value to the invoker
18 return // Return in the normal case
Exception table:
FromTo Target Type
4 10 13 any
13 16 13 any
The compiler ensures that at any method invocation completion, a monitorexit
instruction will have been executed for each monitorenter instruction executed
since the method invocation. This is the case whether the methodinvocation completes
normally (§2.6.4) or abruptly (§2.6.5). To enforce proper pairing of monitorenter
and monitorexit instructions on abruptmethod invocation completion, the
compiler generates exception handlers (§2.10) that will match anyexception and
whose associated code executes the necessary monitorexit instructions.
编译器确认任何方法都被调用完成,monitorexit指令将被执行在每个monitorenter指令被执行以后,这里有个问题,方法是否正常调用完成。或者中断。为确保一对monitorenter和monitorexit指令在方法异常调用完成,编译器生成了一个异常处理器,这个异常处理器会匹配任何异常以及它关联的代码执行必须monitorexit指令。
1.15 Annotations
The representation of annotations in class files is described in §4.7.16and §4.7.17,
which make it clear how to represent annotations on types, fields andmethods in the
class file format. Package annotations require additional rules, givenhere.
When the compiler encounters an annotated package declaration that must be
made available at runtime, it emits a class file that representsan interface whose
name is the internal form (§4.2.1) of package-name.package-info. The interCOMPILING
face has the default access level (“package-private”) and nosuperinterfaces. The
ACC_INTERFACE and ACC_ABSTRACT flags (Table 4.1) of the ClassFile structure
(§4.1) are set. If the emitted class file version number is less than 50.0, thenthe
ACC_SYNTHETIC flag is unset; if the class file version number is50.0 or above,
then the ACC_SYNTHETIC flag is set. The onlymembers of the interface are those
implied by The Java Language Specification, Java SE 7 Edition (JLS §9.2).
The package-level annotations are stored in the RuntimeVisibleAnnotations
(§4.7.16) and RuntimeInvisibleAnnotations (§4.7.17) attributes of the
ClassFile structure (§4.1) of this interface.