Java中异常为什么性能差

Throwable源码

首先给大家看一段JDK的Throwable源码
在这里插入图片描述
上面这段JDK的源码就是抛出异常时会调用的方法,更准确的说是Throwable 的所有构造方法中都会调用fillInStackTrace ,由于所有的异常都直接或间接的继承了Throwable ,所以当我们所以当我们new一个异常的时候,一定会执行fillInStackTrace (创建子类的时候会先执行父类的构造方法)

这个方法暴露出两个问题

  • 使用了synchronized修饰了整个异常方法
  • 需要追踪线程运行堆栈信息

异常种类

  • 业务异常 这些是我们自定义的、可以预知的异常,抛出这种异常并不表示系统出了问题,而是正常业务逻辑上的需要,例如用户名密码错误、参数错误等。
  • 系统异常 往往是运行时异常,比如数据库连接失败、IO异常、空指针等,这种异常产生多数表示系统存在问题,需要人工排查定为。

相信大家都接触过异常,对于业务异常,我们只需要简单的一个描数问题的字符串即可,堆栈追踪信息对我们的意义并不大。而对于系统异常,追踪信息才是排查错误不可或缺的参考。
大家试想,如果前端传的参数错了,系统里就抛出一个异常,那么在双十一的情况下一秒钟得抛出多少个异常呢?
那么有什么办法可以优化异常的性能吗?
答案是有的,很多牛逼的框架自定异常的时候都会覆写fillInStackTrace ,在方法内部直接return this

性能测试

  • 创建1000000次普通的Java异常对象(CustomException1 extands Exception)
  • 创建1000000次改进后的Java异常对象(CustomException2 extands Exception)
public class CustomException1 extends Exception {
}

public class CustomException2 extends Exception {
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}

public class Test {
    public static void main(String[] args) {
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            new CustomException();
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2 - l1);
        for (int i = 0; i < 1000000; i++) {
            new CustomException2();
        }
        long l3 = System.currentTimeMillis();
        System.out.println(l3 - l2);
    }
}

输出结果
在这里插入图片描述
可以看到创建一个普通Exception和覆写了 fillInStackTrace 的Exception,性能差距了很多倍。

其实在Java1.7开始 Throwable 就新增了一个构造方法,供我们选择开启或关闭爬栈
在这里插入图片描述

从代码中可以看到,当writableStackTrace 这个参数为 false 的时候,就不会去调用 fillInStackTeace 方法了。
一般我们的业务异常都是继承自 RuntimeException ,同样也有一个构造方法可以供我们选择开启或关闭爬栈
在这里插入图片描述

问题思考

虽然覆写 fillInStackTrace 或在构造方法种关闭爬栈 可以极大的提高性能,但是我们一定要考虑清楚以下问题

  • 自定义异常是否真的不需要堆栈信息

如果不需要,我个人是强烈推荐覆写掉的。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值