for (int i = 0; i < 1000000; i++) {
try {
Math.sin(j);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
for (int i = 0; i < 1000000; i++) {
Math.sin(j);
}
} catch (Exception e) {
e.printStackTrace();
}
在没有发生异常时,两者性能上没有差异。如果发生异常,两者的处理逻辑不一样,已经不具有比较的意义了。
分析
forTry方法
从输出看,字节码分两部分,code(指令)和exception table(异常表)两部分。
当将java源码编译成相应的字节码的时候,如果方法内有try catch异常处理,就会产生与该方法相关联的异常表,也就是Exception table部分。
异常表记录的是try 起点和终点,catch方法体所在的位置,以及声明捕获的异常种类。
通过这些信息,当程序出现异常时,java虚拟机就会查找方法对应的异常表,如果发现有声明的异常与抛出的异常类型匹配就会跳转到catch处执行相应的逻辑,如果没有匹配成功,就会回到上层调用方法中继续查找,如此反复,一直到异常被处理为止,或者停止进程。
所以,try 在反映到字节码上的就是产生一张异常表,只有发生异常时才会被使用。
try catch与未使用try catch代码区别
try catch阻止Java对try块的代码的一些优化,例如重排序。
try catch里面的代码是不会被编译器优化重排的。对于上面两个函数而言,只是异常表中try起点和终点位置不一样。至于刚刚说到的指令重排的问题,由于for循环条件部分符合happens- before原则,因此两者的for循环都不会发生重排。当然只是针对这里而言,在实际编程中,还是提倡try代码块的范围尽量小,这样才可以充分发挥Java对代码的优化能力。
结论
两者在没有抛出异常时,是没有区别的。
try catch的实质,就是跟方法关联的异常表,在抛出异常的时候,这个就决定了异常是否会被该方法处理。
try catch对性能还是有一定的影响,那就是try块会阻止java的优化(例如重排序)。
当然重排序是需要一定的条件触发。一般而言,只要try块范围越小,对java的优化机制的影响是就越小。所以保证try块范围尽量只覆盖抛出异常的地方,就可以使得异常对java优化的机制的影响最小化。
网友评论