【外刊IT评论】异常的代价

标签: 堆栈跟踪 Stack Traces 技术技巧 异常 | 发表时间:2011-05-24 00:34 | 作者:admin SourBell
出处:http://www.aqee.net
本文是从 The Cost of an Exception 这篇文章翻译而来。

最近在dynaTrace上出现了一场关于异常(Exception)的代价的大讨论。在跟一些客户的接触中,我们经常的发现他们的代码里有大量的异常处理,自己都不知道。在移除了这些异常后,程序的运行速度比以前有大幅度的提高。这让我们产生了一种假想,程序中的异常处理语句是否给性能带来了巨大的开销?由此得出的推理会是,应该避免使用异常处理。由于异常处理是一个非常重要的处理错误情况的概念,完全的避免使用异常处理看起来并不是一种好的办法。总的来说,我们有充足的理由来近距离的观察一下异常的成本代价。

试验

我的试验是一段很简单的代码,随机的抛出异常。这并不是一个具有什么重要意义的性能测量,而且我们也不知道HotSpot编译器在程序运行时会对它做什么操作。不管怎样,这多少会给我们提供一些基本的可观察的信息。

public class ExceptionTest {
 
  public long maxLevel = 20;
 
  public static void main (String ... args){
 
    ExceptionTest test = new ExceptionTest();
 
    long start = System.currentTimeMillis();
    int count = 10000;
    for (int i= 0; i < count; i++){
      try {
        test.doTest(2, 0);
      }catch (Exception ex){
//        ex.getStackTrace();
      }
    }
    long diff = System.currentTimeMillis() - start;
    System.out.println(String.format("Average time for invocation:
%1$.5f",((double) diff)/count));
  }
 
  public void doTest (int i, int level){
      if (level < maxLevel){
        try {
          doTest (i, ++level);
        }
        catch (Exception ex){
//        ex.getStackTrace();
          throw new RuntimeException ("UUUPS", ex);
        }
      }
      else {
        if (i > 1) {
          throw new RuntimeException("Ups".substring(0, 3));
        }
      }
  }
}

结果

结果非常的有趣。抛出和捕捉异常的成本看起来非常的低。在我们这个测试中,每个异常大概用去0.002毫秒。这差不多可以忽略不计,除非你真的抛出了太多的异常——太多是指10万个或更多。

虽然这些结果向我们展示了异常捕捉本身并不会给程序带来性能问题,但它让我们打开了另外一个问题:那究竟是什么使这些异常产生了巨大的性能压力?所以,很显然,我遗漏了某些东西——一些重要的东西。

对这个问题重新思考后,我认识到我遗漏了异常捕捉中的一个重要的部分。我遗漏的这块是当异常发生后人们会做的事情。大多数情况下——希望如此——你并不只是把异常捕捉到,然后就完了。通常你会试图纠正出现的问题,让程序的功能能继续满足最终用户。所以我的遗漏点是捕捉到异常后采用挽救措施的代码。依赖于你的这段代码究竟做了什么,它对性能的影响会有很大的不同。在一些情况下这意味着你需要重新连接某个服务,而另一些情况下可能意味着要调用缺省的预案,可能是一种操作简化的方案。

这对我们在很多场景中见到的现象是一个很好的解释,而我没有做这样的分析。我预感到,应该还有什么东西被我遗漏了。

堆栈跟踪(Stack Traces)

对这个问题好奇不减,我观察了一下当收集这些堆栈跟踪信息时,情况会发生什么变化。这种情况很常见。你会记录一个异常和它的堆栈跟踪信息,用来找出是什么问题。

因此我修改了代码,让它获取异常的堆栈跟踪信息。情况发生了显著的变化。获取异常的堆栈跟踪信息要比只是简单的捕捉、抛出它们能产生10倍大的性能压力。所以,堆栈跟踪信息一方面能帮助我们理解什么地方出了问题甚至为什么会发生这个异常,但同时,它也带来了性能上的惩罚。

这通常对性能的冲击非常的大,因为我们并不是只面对一条堆栈跟踪信息。大多数情况下异常的抛出——捕捉——会发生在多个层级上。让我们看一下一个简单的Web Service连接服务器的例子。首先连接失败的异常会在Java类库基层产生。然后客户端的失败会被框架捕捉到,然后在应用层面上某些业务逻辑调用失败会抛出异常。现在加起来有会收集到3种堆栈跟踪信息。

大多数情况下你会查看这些日志文件和应用输出信息。记录这些很长的堆栈信息也会带来性能上的冲击。如果你有规律的查看你的日志文件,你通常会研究它们,对问题作出反应——这是你要做的事情,不是吗?;-)

有时我还能看到由于不正确的日志写法导致的更严重的性能问题。开发人员不首先调用log.isxxEnabled ()来检查中某个log级别的log行为是否开放,而是直接调用logging方法。当你这样做时,日志代码总是在执行时返回异常的堆栈跟踪信息。但由于日志级别设置的太低,你可能永远看不到这些信息,你可能根本不知道什么事情发生了。首先检查日志记录级别,这应该被当作一个基本习惯,这会让你避免产生不必要的对象。

结论

由于担心造成潜在的性能问题而不使用异常处理是一个不明智的做法。异常提供了一个标准方式来处理运行时的问题,帮助你写出清晰的代码。可是对于这程序中抛出的异常你需要跟踪。尽管异常可以捕捉到,但它们仍然会产生很大的性能压力。在dynaTrace公司,我们——缺省情况下——会跟踪抛出的异常——在很多情况下客户会对程序中的这些代码产生的影响以及解决它们的方法感到很惊讶。

异常处理是个有用的东西,但你应该避免捕捉过多的堆栈跟踪信息。大多数情况下它们对理解问题的发生没有必要的用处——特别是你捕捉一个已经预期到的异常。这时简单异常信息已经足够让你明白问题的原因。看到一个连接被拒绝信息,我已经知道什么问题了,所以我不需要java.net内部调用堆栈跟踪。


本文原始地址:异常的代价

相关 [it 异常] 推荐:

Java异常

- - CSDN博客推荐文章
“好的程序设计语言能够帮助程序员写出好程序,但是无论哪种语言都避免不了程序员写出坏的程序.                                                                                                                          ----《Java编程思想》.

浅谈java异常

- - 移动开发 - ITeye博客
在《java编程思想》中这样定义 异常:阻止当前方法或作用域继续执行的问题. 虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常. 绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败. 之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意.

Oracle 异常处理

- - 编程语言 - ITeye博客
使用RAISE_APPLICATION_ERROR存储过程. ============================================================ */ --演示该存储过程 BEGIN. RAISE_APPLICATION_ERROR(-20000, 'Account past due.');-- explicitly raise exception END; --创建子程序 CREATE OR REPLACE PROCEDURE account_status (.

Google和SugarSync访问异常

- Freeman - 月光博客
  今天下午,Https的Google出现了短时间的访问异常,国内各地网友发现其访问出现不稳定状态,有时可以访问,有时无法访问.   这对中国用户最直接的影响是,很多用户无法访问Https版Google Reader,还有一些用户无法登录Gmail邮箱.   今天的另一个坏消息是,知名美国云存储服务SugarSync网站的域名被关键字屏蔽,目前已经无法从中国访问,虽然SugarSync的上传速度比起Dropbox还有差距,但至少美国的服务用起来放心一些,早先云存储服务Dropbox被屏蔽时不少网友转到了SugarSync这个服务,不过值得庆幸的是,该服务的电脑客户端和iPhone手机端都可以正常使用,上传和下载文件都没有问题.

spring mvc 异常处理(转)

- - 编程语言 - ITeye博客
链接:http://gaojiewyh.iteye.com/blog/1297746 (附源码). 链接:http://zywang.iteye.com/blog/983801 . 链接:http://www.cnblogs.com/xguo/p/3163519.html . 链接:http://fuliang.iteye.com/blog/947191 .

Oracle异常处理概念

- - Oracle - 数据库 - ITeye博客
5.1.1 预定义的异常处理. 5.1.2 非预定义的异常处理. 5.1.3 用户自定义的异常处理. 5.1.4  用户定义的异常处理. 5.2.1 在执行部分引发异常错误. 5.2.2 在声明部分引发异常错误. 5.3 异常错误处理编程. 5.4  在 PL/SQL 中使用 SQLCODE, SQLERRM异常处理函数.

Java异常处理策略

- - 研发管理 - ITeye博客
任务与预先设定的规则不相符的情况都可以称之为异常. 但凡业务逻辑操作,都会划定一些边界或规则,但是往往事与愿违,总会有调皮鬼来挑战系统的健壮性. 用户并不都是知道潜规则的,比如用户的银行账户中只有100块钱,但是用户不查询就直接取200块. 码农有时候太过自信了,比如你在编写文件下载功能时忽略了文件有可能不存在这个分支流程.

【javaScript基础】异常处理

- - CSDN博客Web前端推荐文章
         理解异常在javaScript面向对象编程是非常重要的,异常是一种非常强大的处理错误的方式.          首先我们来看一个有问题的代码:. 在以上这个例子中,访问一个不存在的变量,在这种情况下,程序会怎么处理. 很早以前的处理方式就是程序直接崩溃死掉,所以我们不能容忍这种处理方式,需要有办法来处理.

JVM中的异常处理

- - BlogJava-首页技术区
欢迎来到“ Under The Hood”第六期. 本期我们介绍 JVM处理异常的方式,包括如何抛出和捕获异常及相关的字节码指令. 但本文不会讨论finally子句,这是下期的主题. 你可能需要阅读 往期的文章才能更好的理解本文. 在程序运行时,异常让你可以平滑的处理意外状况. 为了演示JVM处理异常的方式,考虑NitPickyMath类,它提供对整数进行加,减,乘,除以及取余的操作.

service层异常的处理

- - Web前端 - ITeye博客
1、在service方法里面如果对异常进行了捕获的话,该事务是不会进行回滚的.        默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚.   方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在controller层要继续捕获这个异常并处理.