当前位置:  开发笔记 > 编程语言 > 正文

例外有多贵

如何解决《例外有多贵》经验,为你挑选了4个好方法。

你知道java中的异常抛出和处理是多么昂贵吗?

我们在团队中就异常的实际成本进行了多次讨论.有些人认为通过使用例外而导致的性能损失被高估了.

今天我在我们的软件中找到了以下代码:

private void doSomething()
{
    try
    {
      doSomethingElse();
    }
    catch(DidNotWorkException e)
    {
       log("A Message");
    }
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      throw new DidNotWorkException();
   }
   goOnAgain();
}

这与此相比如何表现

private void doSomething()
{
    doSomethingElse();
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      log("A Message");
      return;
   }
   goOnAgain();
}

我不想讨论代码美学或任何东西,它只是关于运行时行为!你有真实的经验/测量吗?



1> TofuBeer..:

例外不是免费的......所以它们很贵:-)

Effective Java这本书详细介绍了这一点.

项目39仅在特殊情况下使用例外.

第40项对可恢复条件使用例外

作者发现,异常导致代码调整速度慢了70倍,因为他的机器上的测试用具有特定的VM和OS组合.



2> Ron..:

抛出异常的最慢部分是填充堆栈跟踪.

如果您预先创建例外并重新使用它,JIT可以将其优化为" 机器级别转到".

所有这一切,除非你的问题中的代码是一个非常紧密的循环,差异将是微不足道的.



3> Esko Luontol..:

关于异常的缓慢部分是构建堆栈跟踪(在构造函数中java.lang.Throwable),这取决于堆栈深度.投掷本身并不慢.

使用例外来表示失败.然后,性能影响可以忽略不计,堆栈跟踪有助于确定故障原因.

如果您需要控制流的异常(不推荐),并且分析显示异常是瓶颈,那么创建一个覆盖fillInStackTrace()空实现的Exception子类.或者(或另外)仅实例化一个异常,将其存储在字段中并始终抛出相同的实例.

下面通过在接受的答案中向微基准添加一个简单的方法(虽然有缺陷),演示了没有堆栈跟踪的异常:

public class DidNotWorkException extends Exception {
  public Throwable fillInStackTrace() {
      return this;
  }
}

-server模式下使用JVM (Windows 7上的版本1.6.0_24)运行它会导致:

Exception:99ms
Boolean:12ms

Exception:92ms
Boolean:11ms

差异很小,在实践中可以忽略不计.



4> Henry B..:

我没有费心去阅读Exceptions但是用你的一些修改过的代码做了一个非常快速的测试我得出结论,Exception情况比布尔情况慢得多.

我得到了以下结果:

Exception:20891ms
Boolean:62ms

从这段代码:

public class Test {
    public static void main(String args[]) {
            Test t = new Test();
            t.testException();
            t.testBoolean();
    }
    public void testException() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomethingException();
            System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms");
    }
    public void testBoolean() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomething();
            System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms");
    }

    private void doSomethingException() {
        try {
          doSomethingElseException();
        } catch(DidNotWorkException e) {
           //Msg
        }
    }
    private void doSomethingElseException() throws DidNotWorkException {
       if(!isSoAndSo()) {
          throw new DidNotWorkException();
       }
    }
    private void doSomething() {
        if(!doSomethingElse())
            ;//Msg
    }
    private boolean doSomethingElse() {
       if(!isSoAndSo())
          return false;
       return true;
    }
    private boolean isSoAndSo() { return false; }
    public class DidNotWorkException extends Exception {}
}

我愚蠢地没有充分阅读我的代码并且之前有一个错误(多么令人尴尬),如果有人可以三重检查这段代码我会非常喜欢它,以防万一我老去了.

我的规格是:

编译并在1.5.0_16上运行

Sun JVM

WinXP SP3

英特尔迅驰双核T7200(2.00Ghz,977Mhz)

2.00 GB Ram

在我看来,您应该注意到非异常方法不会在doSomethingElse中给出日志错误,而是返回一个布尔值,以便调用代码可以处理失败.如果有多个区域可能会失败,则可能需要在内部记录错误或抛出异常.

推荐阅读
郑小蒜9299_941611_G
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有