大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
前言
一朋友问我Try-Catch写多了会不会让程序变慢,我不加思索的回答肯定不会,毕竟曾经研究过Java异常相关的字节码指令,只要被Try-Catch的代码不抛出异常,那么代码执行链路是不会加深的。
可事后我反复思考这个看似简单实则也不复杂的问题,我觉得顺着这个问题往下,还有一些东西可以思考,如果你感兴趣,那就跟随本文的视角一起来看下吧。
正文
首先郑重声明,单纯的针对一段代码添加Try-Catch,是 不会
影响性能的,我们可以通过下面的示例代码并结合字节码指令来看下。
示例代码如下所示。
public class TryCatchPerformance {
public Response execute(String state) {
return innerHandle(state);
}
public Response innerHandle(String state) {
// todo 暂无逻辑
return null;
}
public static class Response {
private int state;
public Response(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
}
我们依次执行如下语句为上述代码生成字节码指令。
# 编译Java文件
javac .\TryCatchPerformance.java
# 反汇编字节码文件
javap -c .\TryCatchPerformance.class
可以得到execute() 方法的字节码指令如下。
public com.lee.learn.exception.TryCatchPerformance$Response execute(java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokevirtual #2 // Method innerHandle:(Ljava/lang/String;)Lcom/lee/learn/exception/TryCatchPerformance$Response;
5: areturn
现在对execute() 方法添加Try-Catch,如下所示。
public class TryCatchPerformance {
public Response execute(String state) {
try {
return innerHandle(state);
} catch (Exception e) {
return new Response(500);
}
}
public Response innerHandle(String state) {
// todo 暂无逻辑
return null;
}
public static class Response {
private int state;
public Response(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
}
查看execute() 方法的字节码指令如下所示。
public com.lee.learn.exception.TryCatchPerformance$Response execute(java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokevirtual #2 // Method innerHandle:(Ljava/lang/String;)Lcom/lee/learn/exception/TryCatchPerformance$Response;
5: areturn
6: astore_2
7: new #4 // class com/lee/learn/exception/TryCatchPerformance$Response
10: dup
11: sipush 500
14: invokespecial #5 // Method com/lee/learn/exception/TryCatchPerformance$Response."<init>":(I)V
17: areturn
Exception table:
from to target type
0 5 6 Class java/lang/Exception
虽然添加Try-Catch后,字节码指令增加了很多条,但是通过Exception table(异常表)我们可知,只有指令0到5在执行过程中抛出了Exception,才会跳转到指令6开始执行,换言之只要不抛出异常,那么在执行完指令5后方法就结束了,此时和没添加Try-Catch时的代码执行链路是一样的,也就是不抛出异常时,Try-Catch不会影响程序性能。
我们添加Try-Catch,其实就是为了做异常处理,也就是我们天然的认为被Try-Catch的代码就是会抛出异常的,而异常一旦发生,此时程序性能就会受到一定程度的影响,表现在如下两个方面。
- 异常对象创建有性能开销。具体表现在异常对象创建时会去爬栈得到方法调用链路信息;
- Try-Catch捕获到异常后会让代码执行链路变深。
由此可见Try-Catch其实不会影响程序性能,但是异常的出现的的确确会影响,无论是JVM创建的异常,还是我们在代码中new出来的异常,都是会影响性能的。
所以现在我们来看看如下代码有什么可以优化的地方。
public class TryCatchPerformance {
public Response execute(String state) {
try {
return innerHandle(state);
} catch (Exception e) {
return new Response(500);
}
}
public Response innerHandle(String state) {
if (state == null || state.isEmpty()) {
// 通过异常中断执行
throw new IllegalStateException();
} else if ("success".equals(state)) {
return new Response(200);
} else {
return new Response(400);
}
}
public static class Response {
private int state;
public Response(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
}
上述代码的问题出现在innerHandle() ,仗着调用方有Try-Catch做异常处理,就在入参非法时通过创建异常来中断执行,我相信在实际的工程开发中,很多时候大家都是这么干的,因为有统一异常处理,那么通过抛出异常来中断执行并在统一异常处理的地方返回响应,是一件再平常不过的事情了,但是通过前面的分析我们知道,创建异常有性能开销,捕获异常并处理也有性能开销,这些性能开销其实是可以避免的,例如下面这样。
public class TryCatchPerformance {
public Response execute(String state) {
try {
return innerHandle(state);
} catch (Exception e) {
return new Response(500);
}
}
public Response innerHandle(String state) {
if (state == null || state.isEmpty()) {
// 通过提前返回响应的方式中断执行
return new Response(500);
} else if ("success".equals(state)) {
return new Response(200);
} else {
return new Response(400);
}
}
public static class Response {
private int state;
public Response(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
}
如果当某个分支执行到了,我们也确切的知道该分支下的响应是什么,此时直接返回响应,相较于抛出异常后在统一异常处理那里返回响应,性能会更好。
总结
Try-Catch其实不会影响程序性能,因为在没有异常发生时,代码执行链路不会加深。
但是如果出现异常,那么程序性能就会受到影响,表现在如下两个方面。
- 异常对象创建有性能开销。具体表现在异常对象创建时会去爬栈得到方法调用链路信息;
- Try-Catch捕获到异常后会让代码执行链路变深。
因此在日常开发中,可以适当增加防御性编程来防止JVM抛出异常,也建议尽量将主动的异常抛出替换为提前返回响应,总之就是尽量减少非必要的异常出现。
大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。