JVMS Specification(2)-Compiling for the Java Virtual Machine

Subsections

2       Compiling for the Java Virtual Machine

2.1        Format of Examples

2.2        Use of Constants, Local Variables, and Control Constructs

2.3        Arithmetic

2.4        Accessing the Runtime Constant Pool

2.5        More Control Examples

2.6        Receiving Arguments

2.7        Invoking Methods

2.8        Working with Class Instances

2.9        Arrays

2.10      CompilingSwitches

2.11      Operationson the Operand Stack

2.12      Throwingand Handling Exceptions

2.13     Compilingfinally

2.14      Synchronization

2.15      Annotations


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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值