重新抛出Java异常与使用instanceof / cast的开销

时间:2014-04-28 18:49:01

标签: java performance exception exception-handling executionexception

我知道Java异常的开销是在SO上完成的,但是我找不到任何可以解决我情况的事情。我有一个Future,在调用get()时可能会抛出包含任意数量的特定于应用程序的异常的ExecutionException。我想知道使用看起来更漂亮的try-catch块而不是丑陋的if-instanceof-then-cast模式是否存在显着的开销。例如,它可能看起来像这样:

private Response handleException(ExecutionException e) throws MyApplicationException {
  try {
    throw e.getCause();
  } catch (ApplicationException1 e1) {
    // known error
    throw MyApplicationException.convert(e1);
  } catch (ApplicationException2 e2) {
    // create error response
    return new Response(e2);
  } catch (Throwable t) {
    // unknown error
    throw new RuntimeException(t);
  }
}

private Response handleException2(ExecutionException e) throws MyApplicationException {
  Throwable cause = e.getCause();
  if (cause instanceof ApplicationException1) {
    ApplicationException1 e1 = (ApplicationException1) cause;
    throw MyApplicationException.convert(e1);
  } else if (cause instanceof ApplicationException2) {
    ApplicationException2 e2 = (ApplicationException2) cause;
    return new Response(e2);
  } else {
    throw new RuntimeException(cause);
  }
}

我的理论是,自

以来不应该有大量的开销
      
  • 已经构建了异常和堆栈跟踪。
  •   
  • 无论如何,我正在对两种方法中的异常对象进行反射。
  •   
  • 异常会立即捕获并且永远不会传播。

3 个答案:

答案 0 :(得分:4)

就样式而言,我通常建议使用异常处理程序进行常规控制流程。我可以在这里看到使用它的论点,因为Future的设计要求你“解包”原始异常。

重新抛出异常应该比抛出新异常要便宜得多,因为已经填充了堆栈跟踪。您的第一种方法可能仍会有更多的开销,但如果您的应用程序抛出了很多例外情况,影响变得明显,那么您可能会遇到更大的问题。

如果这对你真的很重要,那么你获得有意义答案的唯一方法就是自己衡量差异。但是,同样应该只在例外案件中抛出异常;它们在设计上应该是不常见的。即使您将异常处理的成本加倍,成本也只是 2n 而不是 n 。如果你抛出了很多例外,你的应用程序的性能明显受到影响,那么简单的两个因素可能不会让你失败。因此,请使用您认为更具可读性的风格。

答案 1 :(得分:1)

如果您希望第二个示例看起来更好,则可以在使用原因异常时始终进行转换:

private Response handleException2(ExecutionException e) throws MyApplicationException {
  Throwable cause = e.getCause();
  if (cause instanceof ApplicationException1) {
    throw MyApplicationException.convert((ApplicationException1) cause);

  } else if (cause instanceof ApplicationException2) {
    return new Response((ApplicationException2) cause);

  } else {
    throw new RuntimeException(cause);
  }
}

答案 2 :(得分:0)

从原始版本更新 写一些简单的代码使HotSpot编译器没有减少到任何东西是相当具有挑战性的,但我认为以下是好的:

package net.redpoint.utils;
public class Scratch {
    public long counter = 0;
    public class A { 
        public void inc() { counter++; } 
    }
    public class B extends A { 
        public void inc() { counter++; } 
    }
    public class C extends A {
        public void inc() { counter++; } 
    } 
    public A[] a = new A[3];
    public void test() {
        a[0] = new A();
        a[1] = new B();
        a[2] = new C();
        int iter = 100000000;
        long start = System.nanoTime();
        for(int i = iter; i > 0; i--) {
            testUsingInstanceOf(a[i%3]);
        }
        long end = System.nanoTime();
        System.out.println("instanceof: " + iter / ((end - start) / 1000000000.0) + " per second");

        start = System.nanoTime();
        for(int i = iter; i > 0; i--) {
            testUsingException(a[i%3]);
        }
        end = System.nanoTime();
        System.out.println("try{}: " + iter / ((end - start) / 1000000000.0) + " per second");

        start = System.nanoTime();
        for(int i = iter; i > 0; i--) {
            testUsingClassName(a[i%3]);
        }
        end = System.nanoTime();
        System.out.println("classname: " + iter / ((end - start) / 1000000000.0) + " per second");
    }

    public static void main(String[] args) {
        Scratch s = new Scratch();
        s.test();
    }

    public void testUsingInstanceOf(A possiblyB){
        if (possiblyB instanceof B){
            ((B)possiblyB).inc();
        }
    }

    public void testUsingException(A possiblyB){
        try{
            ((B)possiblyB).inc();
        } catch(Exception e){
        }
    }

    public void testUsingClassName(A possiblyB){
        if (possiblyB.getClass().getName().equals("net.redpoint.utils.Scratch$B")){
            ((B)possiblyB).inc();
        }
    }
}

结果输出:

instanceof: 4.573174070960945E8 per second
try{}: 3.926650051387284E8 per second
classname: 7.689439655530204E7 per second

使用带有Intel i7沙桥CPU的Windows 8 x64上的Oracle JRE7 SE进行测试。