2

我知道Java異常的開銷已經在SO上完蛋了,但是我沒有發現任何能解決我的情況的東西。我有一個Future,它在調用get()時可能會拋出一個ExecutionException,其中包含任意數量的特定於應用程序的異常。我想知道使用更好看的try-catch塊而不是醜陋的if-instanceof-then-cast模式是否有顯着的開銷。例如,它可能是這個樣子:重新拋出Java異常和使用instanceof/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); 
    } 
} 

我的理論是不應該有,因爲

  • 異常和堆棧跟蹤已經構建了鉅額的開銷。
  • 無論如何,我在兩種方法中都對異常對象執行反射。
  • 異常立即被捕獲並且永不傳播。
+0

第一個例子的一個缺陷是'getCause()'可以返回'null',並且'ExecutionException'不會覆蓋它。當你拋出'null'時會發生什麼? –

+0

好吧,我可以檢查null(雖然這應該是非常罕見的),但這並不影響我的問題,因爲這只是一個簡單的檢查。 – Nick

+0

你打算在不同情況下做什麼(伐木除外)。我的意思是給你一個例子,而不是你的// ...在catch塊內 – kiruwka

回答

4

至於風格的問題,我一般推薦而不是使用異常處理程序進行常規控制流程。不過,我可以在這裏看到使用它的論點,因爲Future的設計要求您「解開」原始異常。

由於已經填充了堆棧跟蹤,重新拋出異常應該比拋出新異常便宜很多。你的第一種方法可能會有更多的開銷,但是如果你的應用程序拋出了太多的異常以至於影響變得明顯,那麼你可能會遇到更大的問題。

如果真的讓你擔心,唯一能得到有意義答案的方法就是自己衡量差異。但是,再次例外只應在特例個案中引發;他們應該不常見的設計。即使將異常處理的成本加倍,成本也只是2n而不是n。如果你拋出了太多例外,以至於你的應用程序的性能明顯受損,那麼簡單的兩個因素可能不會造成或破壞你。因此,請使用您發現更具可讀性的風格。

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

測試是在Windows 8 x64上使用Oracle JRE7 SE與Intel i7 sand bridge CPU進行的。