2

我知道 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);
  }
}

我的理论是不应该有大量的开销,因为

  • 异常和堆栈跟踪已经构建。
  • 无论如何,我都在这两种方法中对异常对象进行反射。
  • 异常会立即被捕获并且永远不会传播。
4

3 回答 3

4

就风格而言,我通常建议不要将异常处理程序用于常规控制流。不过,我可以在这里看到使用它的论点,因为它的设计Future要求您“解开”原始异常。

重新抛出异常应该比抛出新异常要便宜得多,因为堆栈跟踪已经被填充。您的第一种方法可能仍然存在更多开销,但如果您的应用程序抛出太多异常以至于影响变得明显,那么您可能会遇到更大的问题。

如果这对您来说真的是一个问题,那么您获得有意义答案的唯一方法就是自己衡量差异。但是,同样,异常只应在异常情况下抛出;它们在设计上应该不常见。即使您将异常处理的成本加倍,成本也只是2n而不是n。如果您抛出如此多的异常以致您的应用程序的性能明显受到影响,那么简单的两个因素可能不会成败您。因此,请使用您认为更具可读性的任何样式。

于 2014-04-28T19:41:46.080 回答
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);
  }
}
于 2015-02-24T23:49:31.857 回答
0

UPDATED FROM ORIGINAL It was rather challenging to write trivial code that the HotSpot compiler didn't reduce to nothing, but I think the following is good:

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();
        }
    }
}

The resulting output:

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

Test was performed using Oracle JRE7 SE on Windows 8 x64, with Intel i7 sandy bridge CPU.

于 2015-03-04T15:47:00.187 回答