執行一個catch代碼塊和拋出一個異常花費是很高的,這個過程中的效能損耗主要是由於當建立一個異常時要獲得線程棧的一個快照。拋出異常首先要建立一個新的對象Throwable類的建構函式調用名為fillInStackTrace的方法,fillInStackTrace方法檢查堆棧,收集調用跟蹤資訊。由於在處理過程中建立了一個新的對象,所以說只要有異常被拋出,JVM就必須調整呼叫堆疊,系統資源開銷也就增大了。
1、使編譯器和運行時最佳化,將幾個方法調用放在一個try/catch塊中,而不是為每個方法調用各自使用try/catch塊
try{ Some.method1(); }catch(method1Exception e){ handle exception 1 }try{ Some.method2(); }catch(method2Exception e){ handle exception 2 }try{ Some.method3(); }catch(method3Exception e){ handle exception 3 }
應當最佳化為如下代碼
try{ Some.method1(); Some.method2(); Some.method3(); }catch(method1Exception e){ handle exception 1 }catch(method2Exception e){ handle exception 2 }catch(method3Exception e){ handle exception 3 }
2、異常只能用於錯誤處理,不應該用來控製程序流程。
public class Test { int value; public int getValue() { return value; } public void reset() { value = 0; } // Calculates without exception public void method1(int i) { value = ((value + i) / i) << 1; // Will never be true if ((i & 0xFFFFFFF) == 1000000000) { System.out.println("You'll never see this!"); } } // Could in theory throw one, but never will public void method2(int i) throws Exception { value = ((value + i) / i) << 1; // Will never be true if ((i & 0xFFFFFFF) == 1000000000) { throw new Exception(); } } // This one will regularly throw one public void method3(int i) throws Exception { value = ((value + i) / i) << 1; // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both // an AND operation between two integers. The size of the number plays // no role. AND on 32 BIT always ANDs all 32 bits if ((i & 0x1) == 1) { throw new Exception(); } } public static void main(String[] args) { int i; long l; Test t = new Test(); l = System.currentTimeMillis(); t.reset(); for (i = 1; i < 100000000; i++) { t.method1(i); } l = System.currentTimeMillis() - l; System.out.println( "method1 took " + l + " ms, result was " + t.getValue() ); l = System.currentTimeMillis(); t.reset(); for (i = 1; i < 100000000; i++) { try { t.method2(i); } catch (Exception e) { System.out.println("You'll never see this!"); } } l = System.currentTimeMillis() - l; System.out.println( "method2 took " + l + " ms, result was " + t.getValue() ); l = System.currentTimeMillis(); t.reset(); for (i = 1; i < 100000000; i++) { try { t.method3(i); } catch (Exception e) { // Do nothing here, as we will get here } } l = System.currentTimeMillis() - l; System.out.println( "method3 took " + l + " ms, result was " + t.getValue() ); }}
上段代碼首先建立了三個方法,在這三個方法當中第一個沒有拋出異常,第二個當合格時候拋出異常,第三個和第二個一樣。(實際上三個方法中value的存在就是為了延長程式的已耗用時間,沒有實際意義。)從運行結果可以看出來拋出異常是相當消耗資源的,儘管有時不出現異常時效能尚可,但是依然不建議用異常控製程序流程。
不做多餘的的事情就是調優,保證功能實現的情況下最低程度的減少開銷就是最佳化。