the main bytecode sequence for remainder:
0 iload_0 // push local variable 0 (arg passed as divisor)
1 iload_1 // push local variable 1 (arg passed as dividend)
2 irem // pop divisor, pop dividend, push remainder
3 ireturn // return int on top of stack (the remainder)
the bytecode sequence for the catch (arithmeticexception) clause:
4 pop // pop the reference to the arithmeticexception
// because it isn't used by this catch clause.
5 new #5 <class dividebyzeroexception>
// create and push reference to new object of class
// dividebyzeroexception.
dividebyzeroexception
8 dup // duplicate the reference to the new
// object on the top of the stack because it
// must be both initialized
// and thrown. the initialization will consume
// the copy of the reference created by the dup.
9 invokenonvirtual #9 <method dividebyzeroexception.<init>()v>
// call the constructor for the dividebyzeroexception
// to initialize it. this instruction
// will pop the top reference to the object.
12 athrow // pop the reference to a throwable object, in this
// case the dividebyzeroexception,
// and throw the exception.
exception table:
from to target type
0 4 4 <class java.lang.arithmeticexception>
上面的异常表指示从pc偏移0到3(包括0),表示arithmeticexception将被捕获的范围。在标签“to”下面的表中列出的是try块的端点值,它总是比捕获异常的最后一个pc偏移量多一。在这种情况下,端点值列为4,捕获到异常的最后一个pc偏移量为3。此范围(包括0到3)对应于在remainder的try块内实现代码的字节码序列。如果arithmeticexception在pc偏移量为0和3之间(包括0和3)之间抛出,则表中列出的"to"就是跳转到的pc偏移量。
如果在执行方法期间抛出异常,java虚拟机将在异常表中搜索匹配的条目。如果当前程序计数器在条目指定的范围内,并且抛出的异常类是由条目指定的异常类(或者是指定异常类的子类),则异常表条目匹配。java虚拟机按照条目在表中的显示顺序搜索异常表。找到第一个匹配项后,java虚拟机会将程序计数器设置为新的pc偏移位置并继续执行。如果未找到匹配项,java虚拟机将弹出当前堆栈帧并重新抛出相同的异常。当java虚拟机弹出当前堆栈帧时,它有效地中止当前方法的执行并返回调用此方法的方法。但是,不是在前一个方法中继续正常执行,而是在该方法中抛出相同的异常,这会导致java虚拟机经历搜索该方法的异常表的相同过程。
java程序员可以使用throw语句抛出异常,例如remainder中的一个子句catch(arithmeticexception),其中一个 dividebyzeroexception创建并抛出。执行抛出的字节码如下表所示:
opcodeoperand(s)descriptionathrow(none)pops throwable object reference, throws the exceptionathrow指令从堆栈中弹出顶部字节,并且会认为它是一个throwable子类的引用(或throwable本身)。抛出的异常是弹出对象引用定义的类型。 play ball!: a java virtual machine simulation
下面的applet演示了一个执行一系列字节码的java虚拟机。模拟中的字节码序列由javac生成。
类的playball方法如下所示:
class ball extends exception {
}
class pitcher {
private static ball ball = new ball();
static void playball() {
int i = 0;
while (true) {
try {
if (i % 4 == 3) {
throw ball;
}
++i;
}
catch (ball b) {
i = 0;
}
}
}
}
javac为该playball方法生成的字节码如下所示:
0 iconst_0 // push constant 0
1 istore_0 // pop into local var 0: int i = 0;
// the try block starts here (see exception table, below).
2 iload_0 // push local var 0
3 iconst_4 // push constant 4
4 irem // calc remainder of top two operands
5 iconst_3 // push constant 3
6 if_icmpne 13 // jump if remainder not equal to 3: if (i % 4 == 3) {
// push the static field at constant pool location #5,
// which is the ball exception itching to be thrown
9 getstatic #5 <field pitcher.ball lball;>
12 athrow // heave it home: throw ball;
13 iinc 0 1 // increment the int at local var 0 by 1: ++i;
// the try block ends here (see exception table, below).
16 goto 2 // jump always back to 2: while (true) {}
// the following bytecodes implement the catch clause:
19 pop // pop the exception reference because it is unused
20 iconst_0 // push constant 0
21 istore_0 // pop into local var 0: i = 0;
22 goto 2 // jump always back to 2: while (true) {}
exception table:
from to target type
2 16 19 <class ball>
```