上一节总结了方法调用,在类信息中的方法表中会标记目标方法的地址信息,这个地址信息一开始是符号引用,比如(类名+方法名+参数+返回参数)符号。在运行时必须要解析为实际的内存地址,那么这个过程是分为动态绑定和静态绑定,动态绑定也可以根据单态缓存来提高性能。这一节我们来了解一下异常处理的流程。
jvm的异常体系
Error:特别严重且无法修复的异常
Exception:可以挽救的异常
- try-catch:在方法执行到RuntimeException时,如果有异常处理代码(try-catch)时就会查看是否catch的异常类型匹配,如果匹配则执行catch代码,不匹配直接弹出栈帧,访问调用方法的try-catch。栈帧遍历完毕。
- finally: 在try-catch的后面如果有finally,则在当前栈帧弹出之前,执行finally的代码,即几种情况,
- 无异常发生,则在try代码段之后执行,
- 有异常发生,则在catch方法代码段执行之后执行finally代码。
- catch代码段执行异常,则执行finally的代码,并抛出catch代码的异常,意味着要覆盖try代码的异常
- finally代码执行异常,继续抛出异常
查看例子:
class Demo{
public void sayHi(){
try{
//正常业务处理
sout("heihei");
}catch(NoSuchMethodException e){
//处理异常
handleException(e);
}finally{
//关闭资源
closeResource();
}
}
}
在代码中有try catch finally三个部分。但是经过编译器之后,jvm会重新排列上面的代码,
Classfile /Users/david/Desktop/techfile/demo/eurekas/clients/src/test/java/Father.class
Last modified 2020-10-15; size 710 bytes
MD5 checksum 31051ef6ce716bf8e8cd4e884013000e
Compiled from "Father.java"
public class Father
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#25 // java/lang/Object."<init>":()V
#2 = String #26 // heihei
#3 = Methodref #7.#27 // Father.sout:(Ljava/lang/String;)V
#4 = Methodref #7.#28 // Father.closeResource:()V
#5 = Class #29 // java/lang/NoSuchMethodException
#6 = Methodref #7.#30 // Father.handleException:(Ljava/lang/NoSuchMethodException;)V
#7 = Class #31 // Father
#8 = Class #32 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 sayHi
#14 = Utf8 StackMapTable
#15 = Class #29 // java/lang/NoSuchMethodException
#16 = Class #33 // java/lang/Throwable
#17 = Utf8 closeResource
#18 = Utf8 handleException
#19 = Utf8 (Ljava/lang/NoSuchMethodException;)V
#20 = Utf8 sout
#21 = Utf8 (Ljava/lang/String;)V
#22 = Utf8 Exceptions
#23 = Utf8 SourceFile
#24 = Utf8 Father.java
#25 = NameAndType #9:#10 // "<init>":()V
#26 = Utf8 heihei
#27 = NameAndType #20:#21 // sout:(Ljava/lang/String;)V
#28 = NameAndType #17:#10 // closeResource:()V
#29 = Utf8 java/lang/NoSuchMethodException
#30 = NameAndType #18:#19 // handleException:(Ljava/lang/NoSuchMethodException;)V
#31 = Utf8 Father
#32 = Utf8 java/lang/Object
#33 = Utf8 java/lang/Throwable
{
public Father();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
public void sayHi();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: ldc #2 // String heihei
3: invokespecial #3 // Method sout:(Ljava/lang/String;)V
6: aload_0
7: invokespecial #4 // Method closeResource:()V
10: goto 33
13: astore_1
14: aload_0
15: aload_1
16: invokespecial #6 // Method handleException:(Ljava/lang/NoSuchMethodException;)V
19: aload_0
20: invokespecial #4 // Method closeResource:()V
23: goto 33
26: astore_2
27: aload_0
28: invokespecial #4 // Method closeResource:()V
31: aload_2
32: athrow
33: return
Exception table:
from to target type
0 6 13 Class java/lang/NoSuchMethodException
0 6 26 any
13 19 26 any
LineNumberTable:
line 12: 0
line 18: 6
line 19: 10
line 13: 13
line 15: 14
line 18: 19
line 19: 23
line 18: 26
line 19: 31
line 21: 33
StackMapTable: number_of_entries = 3
frame_type = 77 /* same_locals_1_stack_item */
stack = [ class java/lang/NoSuchMethodException ]
frame_type = 76 /* same_locals_1_stack_item */
stack = [ class java/lang/Throwable ]
frame_type = 6 /* same */
}
SourceFile: "Father.java"
jvm会在操作命令后面增加异常处理表,
Exception table:
from to target type
0 6 13 Class java/lang/NoSuchMethodException
0 6 26 any
13 19 26 any
上面的字节码表明需要监控sayHi方法中code的执行条数,0-6之间如果发生NoSuchMethodException异常就到target指向的目录执行代码。0-6还监控了any代表的任何异常,这是finally的功劳