异常表
每一个 try语句块 catch的异常都与异常表中的一项相对应,异常表中的每一项都包括:
-
起点
-
终点,始终把 catch异常位置的 pc指针偏移量的最大值大1
-
处理异常时跳转到的字节码序列中的 pc指针偏移量
-
被 catch的异常类的常量池索引
例如:

public
class
Test
{
public
static
void
main(String[] args)
{

try
{
Class.forName(
"
java.lang.String
"
);
}
catch
(ClassNotFoundException e)
{
e.printStackTrace();
}

}
}
用 javap –c 查看字节码如下:
Compiled from
"
Test.java
"

public
class
Test
extends
java.lang.Object
{
public
Test();
Code:
0
: aload_0
1
: invokespecial #
1
;
//
Method java/lang/Object."<init>":()V
4
:
return

public
static
void
main(java.lang.String[]);
Code:
0
: ldc #
2
;
//
String java.lang.String
2
: invokestatic #
3
;
//
Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
5
: pop
6
:
goto
14
9
: astore_1
10
: aload_1
11
: invokevirtual #
5
;
//
Method java/lang/ClassNotFoundException.printStackTrace:()V
14
:
return
Exception table:
from to target type
0
6
9
Class java
/
lang
/
ClassNotFoundException
}
可见 ClassNotFoundException异常可能会在 0~6之间抛出, 9开始处的代码处理此异常。
当产生异常的时候, jvm将会在整个异常表中搜索与之匹配的项,如果当前 pc在异常表入口所指的范围内,并且所抛出的异常是此入口所指向的类或者其子类,则跳转到对应的处理代码继续执行。
方法可能会抛出哪些已检查异常
Class文件的 attribute_info中保存有 Exceptions属性,记录着每个方法 throws的异常信息。具体的可以查看 class类文件格式相关的文章。
athrow指令从栈顶弹出 Throwable对象引用,抛出异常。
finally语句
在 jvm规范中, finally语句是通过 jsr/jsr_w与 ret指令实现的。当执行 jsr/jsr_w的时候将 finally执行完成后的返回地址压入栈中,进入 finally后会马上将此地址保存到一个局部变量中,执行完成后, ret从此局部变量中取出返回地址。???为什么会先把返回地址保存到局部变量中呢???因为,当从 finally语句返回的时候需要将返回地址成栈中弹出,当 finally语句非正常结束 (break,continue,return, 抛异常 )的时候就不用再考虑这个问题。
以下是 jvm 规范中 Compiling finally 的一段:

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
当 tryItOut排除任何异常后都将会被异常表中的 any项捕获,执行完 finally后,会执行 athrow指令将异常抛出。
从 jdk的某一个版本开始就不会编译出编译出含 jsr/jsr_w、 ret的字节码了,因为有指令上的缺陷,导致 jvm的检验和分析系统出现漏洞。
再说 finally的非正常退出
在 finally中使用 break、 continue、 return、抛出异常等认为是 finally的非正常结束。非正常结束的时候, ret指令不会被执行,很可能会出现意想不到的结果。如:

public
class
Test
{
public
static
boolean
test(
boolean
b)
{
while
(b)
{
try
{
return
true
;
}
finally
{
/**/
/*
break; 始终返回false
continue; javac编译再java执行会出现死循环
在eclipse中甚至会出现报错:提示找到不main class
return false; 始终返回false
throw new RuntimeException(""); 抛出异常
*/
}
}

return
false
;
}


public
static
void
main(String[] args)
{
System.out.println(test(
true
));
}
}
建议:在写
finally
语句的时候,尽量避免非正常结束!
2万+

被折叠的 条评论
为什么被折叠?



