11月
18
2013

消除Java应用中的Exception开销

抛异常最大的消耗在于构造整个异常栈的过程,如果你的栈很深,特别是用了一些框架的话,这个开销基本是不可忽视的,之前做的一个优化显示当时应用中的一个异常使得整个应用的性能下降至少30%。

一、开销在哪

最大开销的地方在这里,当你去new一个Exception的时候,会调用父类Throwable的构造函数,Throwable的构造函数中会调用native的fillInStackTrace(),这个方法就会构造整个异常栈了

二、优化方案

要优化这个地方,最简单的方案有两种:

1、去掉异常栈构造

如果你的Exception是自定义类型的,你很清楚什么情况哪行代码会抛出这个Exception,例如限流或者拒绝服务了。那么你可以给你的Exception Class重写fillInStackTrace()这个方法,搞一个空的实现就可以了,这样构造函数去调的时候就不会真正去调那个native的方法。抛异常的开销就没那么大了。

2、去掉异常

更推荐的做法是去看你到底是不是真的需要抛异常,把没有意义的异常去掉。例如之前见过有代码用异常流来实现正常的业务逻辑的,这种性能就很差了,因为你每次调用都会至少抛一个异常,并发量大的时候对性能影响非常大。

三、寻找异常

有时候你可能无法直接找到到底哪个异常抛得最多,最典型的是引入了一个三方包,它里面自己抛了异常然后又自己catch住了,外面压根不知道里面居然还有这些破事儿。

可以用perf top去看下us的开销,如果_ZN19java_lang_Throwable19fill_in_stack_traceE6HandleP6Thread这个排名很靠前,那就有必要看看异常的大头在哪里。

最简单的用BTrace去跟一下Exception.<init>看构造Exception的栈是什么样的,然后排序汇总一下,一般就能看到什么Excetion最多,是谁抛的,然后有针对性地把它们去掉或者优化掉。

然后再压测你的应用,对CPU的开销会减少不少。

Written by Hesey Wang in: Java,技术 |

10 Comments »

  • Viva

    我司就有个并发应用在用“异常流”处理业务,但是因为历史原因,没人敢改,我也被勒令不要动这段代码。于是只能呵呵了。方法二其实是common sense,方法一受教了,感谢!

    [回复]

    cellardoor 回复:

    使用异常控(自定义ServiceException)制业务流程,有几个优点:
    1,可以使用断言,减少if else;可以不用对服务方法设计统一的返回值;增强代码的可读性,扩展性。
    2,使用切面技术对该异常进行监控,也就对我们的服务的整体运行情况有了监控。
    3,对在客户端,对服务异常的处理也可以用一个整体框架进行反转控制。

    缺点:性能。
    解决:
    1,ServiceException并不构建异常堆栈,而是保存失败原因FailCause或者FailID。(这些都是自己业务框架定义的“小”对象)。
    2,重写fillInStackTrace方法进行noop。

    凡事是有利也有弊,取其利,避其弊。

    [回复]

    zongzhe.hu 回复:

    jdk1.7中提供了一个方法,不用重写吧?
    protected RuntimeException(String message, Throwable cause,
    boolean enableSuppression,
    boolean writableStackTrace) {
    super(message, cause, enableSuppression, writableStackTrace);
    }

    [回复]

    Comment | 2013 年 11 月 20 日
  • “寻找异常”这一部分能稍微说的更加细一点吗

    [回复]

    Comment | 2013 年 11 月 21 日
  • 匿名

    去掉异常栈构造是这样?
    public MyException extends Exception{
    public Throwable fileInStackTrace(){
    // …
    }
    }

    [回复]

    Comment | 2014 年 03 月 14 日
  • karott

    貌似我们就是在用大量的异常流处理业务逻辑

    [回复]

    Comment | 2014 年 05 月 08 日
  • cellardoor

    使用异常控(自定义ServiceException)制业务流程,有几个优点:
    1,可以使用断言,减少if else;可以不用对服务方法设计统一的返回值;增强代码的可读性,扩展性。
    2,使用切面技术对该异常进行监控,也就对我们的服务的整体运行情况有了监控。
    3,对在客户端,对服务异常的处理也可以用一个整体框架进行反转控制。

    缺点:性能。
    解决:
    1,ServiceException并不构建异常堆栈,而是保存失败原因FailCause或者FailID。(这些都是自己业务框架定义的“小”对象)。
    2,重写fillInStackTrace方法进行noop。

    凡事是有利也有弊,取其利,避其弊。

    [回复]

    Hesey Wang 回复:

    异常情况的返回,ResultCode或者Exception算是两个大流派吧,如果ResuleCode能适合你的场景,我觉得还是把Exception留给真正的“异常”:)

    [回复]

    Silence Wang 回复:

    拜读啦这篇文章,之前没有注意仔细分析过异常的具体开销大头在哪,只是网上人云亦云说exception性能差,开发中就尽量不用,以至于,开发架构时所有方法选择一个包装类,比如:Result,里面放一个resultCode,message,result;以至于调用的代码,每次都得check,太不优雅啦!下回重写fillInStackTrace(),嘿嘿!博主让我认识到,好的程序对代码细节的追求,之前有意识可从没行动过比如就这个异常性能开销问题。在此表示感谢!!!

    [回复]

    Comment | 2014 年 05 月 09 日
  • 公司一直都是提倡用ResultCode,不过第一种方式不错哦

    [回复]

    Comment | 2014 年 09 月 12 日

RSS feed for comments on this post. TrackBack URL

Leave a comment

©2006 - 2016 Hesey (舒)