概览
从 Instruction 的角度来说,与 exception 相关的 opcode 有 1 个,内容如下:
opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol |
---|---|---|---|---|---|---|---|
191 | athrow |
从 ASM 的角度来说,这个 athrow
指令与 MethodVisitor.visitXxxInsn()
方法对应关系如下:
MethodVisitor.visitInsn()
:athrow
throw exception
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test() {
throw new RuntimeException();
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c -p sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test();
Code:
0: new #2 // class java/lang/RuntimeException
3: dup
4: invokespecial #3 // Method java/lang/RuntimeException."<init>":()V
7: athrow
}
从 ASM 的视角来看,方法体对应的内容如下:
methodVisitor.visitCode();
methodVisitor.visitTypeInsn(NEW, "java/lang/RuntimeException");
methodVisitor.visitInsn(DUP);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "()V", false);
methodVisitor.visitInsn(ATHROW);
methodVisitor.visitMaxs(2, 1);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this} | {}
0000: new #2 // {this} | {uninitialized_RuntimeException}
0003: dup // {this} | {uninitialized_RuntimeException, uninitialized_RuntimeException}
0004: invokespecial #3 // {this} | {RuntimeException}
0007: athrow // {} | {}
从 JVM 规范的角度来看,athrow
指令对应的 Operand Stack 的变化如下:
..., objectref →
objectref
The objectref
must be of type reference
and must refer to an object that is an instance of class Throwable
or of a subclass of Throwable
. It is popped from the operand stack. The objectref
is then thrown by searching the current method for the first exception handler that matches the class of objectref
.
- If an exception handler that matches
objectref
is found, it contains the location of the code intended to handle this exception. Thepc
register is reset to that location, the operand stack of the current frame is cleared,objectref
is pushed back onto the operand stack, and execution continues. - If no matching exception handler is found in the current frame, that frame is popped. Finally, the frame of its invoker is reinstated, if such a frame exists, and the
objectref
is rethrown. If no such frame exists, the current thread exits.
catch exception
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test() {
try {
int val = 1 / 0;
}
catch (ArithmeticException e1) {
System.out.println("catch ArithmeticException");
}
catch (Exception e2) {
System.out.println("catch Exception");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c -p sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test();
Code:
0: iconst_1
1: iconst_0
2: idiv
3: istore_1
4: goto 28
7: astore_1
8: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #4 // String catch ArithmeticException
13: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: goto 28
19: astore_1
20: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #7 // String catch Exception
25: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
Exception table:
from to target type
0 4 7 Class java/lang/ArithmeticException
0 4 19 Class java/lang/Exception
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
Label label4 = new Label();
methodVisitor.visitCode();
methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/ArithmeticException");
methodVisitor.visitTryCatchBlock(label0, label1, label3, "java/lang/Exception");
methodVisitor.visitLabel(label0);
methodVisitor.visitInsn(ICONST_1);
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitInsn(IDIV);
methodVisitor.visitVarInsn(ISTORE, 1);
methodVisitor.visitLabel(label1);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label2);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("catch ArithmeticException");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label3);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("catch Exception");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label4);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this} | {}
0000: iconst_1 // {this} | {int}
0001: iconst_0 // {this} | {int, int}
0002: idiv // {this} | {int}
0003: istore_1 // {this, int} | {}
0004: goto 24 // {} | {}
// {this} | {ArithmeticException}
0007: astore_1 // {this, ArithmeticException} | {}
0008: getstatic #3 // {this, ArithmeticException} | {PrintStream}
0011: ldc #4 // {this, ArithmeticException} | {PrintStream, String}
0013: invokevirtual #5 // {this, ArithmeticException} | {}
0016: goto 12 // {} | {}
// {this} | {Exception}
0019: astore_1 // {this, Exception} | {}
0020: getstatic #3 // {this, Exception} | {PrintStream}
0023: ldc #7 // {this, Exception} | {PrintStream, String}
0025: invokevirtual #5 // {this, Exception} | {}
// {this} | {}
0028: return // {} | {}