<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    stone2083

    Java Exception性能問題

    背景:
    大學里學java,老師口口聲聲,言之鑿鑿,告誡我們,Java千萬別用異??刂茦I務流程,只有系統級別的問題,才能使用異常;
    (當時,我們都不懂為什么不能用異常,只知道老師這么說,我們就這么做,考試才不會錯 :) )
    公司里,有兩派.異常擁護者說,使用業務異常,代碼邏輯更清晰,更OOP;反之者說,使用異常,性能非常糟糕;
    (當然,我是擁護者)
    論壇上,爭論得更多,仁者見仁智者見智,口水很多;
    (我不發表意見,其實怎么用,真的都可以)

    那么,為什么反對異常呢?貌似大多數人的原因都只有一個:性能差!
    使用異常性能真的差嗎? 是的!
    是什么原因,導致性能問題呢? 那么請看下文...

    根本原因在于:
    異?;怲hrowable.java的public synchronized native Throwable fillInStackTrace()方法
    方法介紹:
    Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread.
    性能開銷在于:
    1. 是一個synchronized方法(主因)
    2. 需要填充線程運行堆棧信息

    但是對于業務異常來說,它只代表業務分支;壓根兒不需要stack信息的.如果去掉這個過程,是不是有性能提升呢?

    于是做了一個簡單的測試對比,對比主體:
    1。 創建普通Java對象              (CustomObject extends HashMap)
    2。 創建普通Java異常對象          (CustomException extends Exception)
    3。 創建改進的Java業務異常對象    (CustomException extends Exception,覆寫fillInStackTrace方法,并且去掉同步)

    測試結果:
    (運行環境:xen虛擬機,5.5G內存,8核;jdk1.6.0_18)
    (10個線程,創建10000000個對象所需時間)
    普通Java對象         45284 MS
    普通java異常        205482 MS
    改進的Java業務異常   16731 MS

    測試代碼如下:
      1 /**
      2  * <pre>
      3  * xen虛擬機,5.5G內存;8核CPU
      4  * LOOP = 10000000
      5  * THREADS = 10
      6  * o:       45284 
      7  * e:       205482 
      8  * exte:    16731
      9  * </pre>
     10  * 
     11  * k
     12  * 
     13  * @author li.jinl 2010-7-9 上午09:16:14
     14  */
     15 public class NewExceptionTester {
     16 
     17     private static final int             LOOP                 = 10000000;                        // 單次循環數量
     18     private static final int             THREADS              = 10;                              // 并發線程數量
     19 
     20     private static final List<Long>      newObjectTimes       = new ArrayList<Long>(THREADS);
     21     private static final List<Long>      newExceptionTimes    = new ArrayList<Long>(THREADS);
     22     private static final List<Long>      newExtExceptionTimes = new ArrayList<Long>(THREADS);
     23 
     24     private static final ExecutorService POOL                 = Executors.newFixedThreadPool(30);
     25 
     26     public static void main(String[] args) throws Exception {
     27         List<Callable<Boolean>> all = new ArrayList<Callable<Boolean>>();
     28         all.addAll(tasks(new NewObject()));
     29         all.addAll(tasks(new NewException()));
     30         all.addAll(tasks(new NewExtException()));
     31 
     32         POOL.invokeAll(all);
     33 
     34         System.out.println("o:\t\t" + total(newObjectTimes));
     35         System.out.println("e:\t\t" + total(newExceptionTimes));
     36         System.out.println("exte:\t\t" + total(newExtExceptionTimes));
     37 
     38         POOL.shutdown();
     39     }
     40 
     41     private static List<Callable<Boolean>> tasks(Callable<Boolean> c) {
     42         List<Callable<Boolean>> list = new ArrayList<Callable<Boolean>>(THREADS);
     43         for (int i = 0; i < THREADS; i++) {
     44             list.add(c);
     45         }
     46         return list;
     47     }
     48 
     49     private static long total(List<Long> list) {
     50         long sum = 0;
     51         for (Long v : list) {
     52             sum += v;
     53         }
     54         return sum;
     55     }
     56 
     57     public static class NewObject implements Callable<Boolean> {
     58 
     59         @Override
     60         public Boolean call() throws Exception {
     61             long start = System.currentTimeMillis();
     62             for (int i = 0; i < LOOP; i++) {
     63                 new CustomObject("");
     64             }
     65             newObjectTimes.add(System.currentTimeMillis() - start);
     66             return true;
     67         }
     68 
     69     }
     70 
     71     public static class NewException implements Callable<Boolean> {
     72 
     73         @Override
     74         public Boolean call() throws Exception {
     75             long start = System.currentTimeMillis();
     76             for (int i = 0; i < LOOP; i++) {
     77                 new CustomException("");
     78             }
     79             newExceptionTimes.add(System.currentTimeMillis() - start);
     80             return true;
     81         }
     82 
     83     }
     84 
     85     public static class NewExtException implements Callable<Boolean> {
     86 
     87         @Override
     88         public Boolean call() throws Exception {
     89             long start = System.currentTimeMillis();
     90             for (int i = 0; i < LOOP; i++) {
     91                 new ExtCustomException("");
     92             }
     93             newExtExceptionTimes.add(System.currentTimeMillis() - start);
     94             return true;
     95         }
     96 
     97     }
     98 
     99     /**
    100      * 自定義java對象.
    101      * 
    102      * @author li.jinl 2010-7-9 上午11:28:27
    103      */
    104     public static class CustomObject extends HashMap {
    105 
    106         private static final long serialVersionUID = 5176739397156548105L;
    107 
    108         private String            message;
    109 
    110         public CustomObject(String message){
    111             this.message = message;
    112         }
    113 
    114         public String getMessage() {
    115             return message;
    116         }
    117 
    118         public void setMessage(String message) {
    119             this.message = message;
    120         }
    121 
    122     }
    123 
    124     /**
    125      * 自定義普通的Exception對象
    126      * 
    127      * @author li.jinl 2010-7-9 上午11:28:58
    128      */
    129     public static class CustomException extends Exception {
    130 
    131         private static final long serialVersionUID = -6879298763723247455L;
    132 
    133         private String            message;
    134 
    135         public CustomException(String message){
    136             this.message = message;
    137         }
    138 
    139         public String getMessage() {
    140             return message;
    141         }
    142 
    143         public void setMessage(String message) {
    144             this.message = message;
    145         }
    146 
    147     }
    148 
    149     /**
    150      * <pre>
    151      * 自定義改進的Exception對象 覆寫了 fillInStackTrace方法
    152      * 1. 不填充stack
    153      * 2. 取消同步
    154      * </pre>
    155      * 
    156      * @author li.jinl 2010-7-9 上午11:29:12
    157      */
    158     public static class ExtCustomException extends Exception {
    159 
    160         private static final long serialVersionUID = -6879298763723247455L;
    161 
    162         private String            message;
    163 
    164         public ExtCustomException(String message){
    165             this.message = message;
    166         }
    167 
    168         public String getMessage() {
    169             return message;
    170         }
    171 
    172         public void setMessage(String message) {
    173             this.message = message;
    174         }
    175 
    176         @Override
    177         public Throwable fillInStackTrace() {
    178             return this;
    179         }
    180     }
    181 }



    所以,如果我們業務異常的基類,一旦覆寫fillInStackTrace,并且去掉同步,那么異常性能有大幅度提升(因為業務異常本身也不需要堆棧信息)


    如果說,創建異常的性能開銷大家已經有些感覺了,那么TryCatch是否也存在性能開銷呢?
    接下來,做了一次try...catch 和 if...esle的性能比較

    測試結果(運行環境和上面一樣):
    20個線程,100000000,所消耗的時間:
    try...catch:  101412MS
    if...else:    100749MS

    備注:
    在我自己的開發機器上(xp和ubuntu下,單核),try...catch耗時是if...else的2倍(在同一數量級)
    具體原因還未知,之后會使用專業的性能測試工具進行分析

    測試代碼如下:
      1 /**
      2  * <pre>
      3  * xen虛擬機,5.5G內存;8核CPU
      4  * LOOP = 100000000
      5  * THREADS = 20
      6  * 
      7  * tc:  101412
      8  * ie:  100749
      9  * </pre>
     10  * 
     11  * @author li.jinl 2010-7-9 上午10:47:56
     12  */
     13 public class ProcessTester {
     14 
     15     private static final int             LOOP          = 100000000;
     16     private static final int             THREADS       = 20;
     17 
     18     private static final List<Long>      tryCatchTimes = new ArrayList<Long>(THREADS);
     19     private static final List<Long>      ifElseTimes   = new ArrayList<Long>(THREADS);
     20 
     21     private static final ExecutorService POOL          = Executors.newFixedThreadPool(40);
     22 
     23     public static void main(String[] args) throws Exception {
     24         List<Callable<Boolean>> all = new ArrayList<Callable<Boolean>>();
     25         all.addAll(tasks(new TryCatch()));
     26         all.addAll(tasks(new IfElse()));
     27 
     28         POOL.invokeAll(all);
     29 
     30         System.out.println("tc:\t\t" + total(tryCatchTimes));
     31         System.out.println("ie:\t\t" + total(ifElseTimes));
     32 
     33         POOL.shutdown();
     34     }
     35 
     36     private static List<Callable<Boolean>> tasks(Callable<Boolean> c) {
     37         List<Callable<Boolean>> list = new ArrayList<Callable<Boolean>>(THREADS);
     38         for (int i = 0; i < THREADS; i++) {
     39             list.add(c);
     40         }
     41         return list;
     42     }
     43 
     44     private static long total(List<Long> list) {
     45         long sum = 0;
     46         for (Long v : list) {
     47             sum += v;
     48         }
     49         return sum;
     50     }
     51 
     52     public static class TryCatch implements Callable<Boolean> {
     53 
     54         @Override
     55         public Boolean call() throws Exception {
     56             long start = System.currentTimeMillis();
     57             for (int i = 0; i < LOOP; i++) {
     58                 try {
     59                     exception();
     60                     // 
     61                 } catch (ExtCustomException e) {
     62                     // 
     63                 }
     64             }
     65             tryCatchTimes.add(System.currentTimeMillis() - start);
     66             return true;
     67         }
     68 
     69         private void exception() throws ExtCustomException {
     70             throw new ExtCustomException("");
     71         }
     72 
     73     }
     74 
     75     public static class IfElse implements Callable<Boolean> {
     76 
     77         @Override
     78         public Boolean call() throws Exception {
     79             long start = System.currentTimeMillis();
     80             for (int i = 0; i < LOOP; i++) {
     81                 Exception e = exception();
     82                 if (e instanceof ExtCustomException) {
     83                     // 
     84                 }
     85             }
     86             ifElseTimes.add(System.currentTimeMillis() - start);
     87             return true;
     88         }
     89 
     90         private Exception exception() {
     91             return new ExtCustomException("");
     92         }
     93 
     94     }
     95 
     96     public static class ExtCustomException extends Exception {
     97 
     98         private static final long serialVersionUID = -6879298763723247455L;
     99 
    100         private String            message;
    101 
    102         public ExtCustomException(String message){
    103             this.message = message;
    104         }
    105 
    106         public String getMessage() {
    107             return message;
    108         }
    109 
    110         public void setMessage(String message) {
    111             this.message = message;
    112         }
    113 
    114         @Override
    115         public Throwable fillInStackTrace() {
    116             return this;
    117         }
    118 
    119     }
    120 
    121 }


    結論:
    1。Exception的性能是差,原因在于ThrowablefillInStackTrace()方法
    2. 可以通過改寫業務異常基類的方法,提升性能
    3。try...catch和if...else的性能開銷在同一數量級

    4。至于是否使用異常進行業務邏輯的控制,主要看代碼風格.(我個人挺喜歡業務異常的)


    備注:
    以上測試比較簡單,寫得也比較急.此文也寫得比較急(工作時間偷偷寫).如果分析不到位的地方,請指出.

    posted on 2010-07-09 14:30 stone2083 閱讀(13746) 評論(16)  編輯  收藏 所屬分類: java

    Feedback

    # re: Java Exception性能問題[未登錄] 2010-07-09 16:50 Jacky

    值得借鑒 3Q  回復  更多評論   

    # re: Java Exception性能問題 2010-07-09 16:54 去改變

    學習了  回復  更多評論   

    # re: Java Exception性能問題 2010-07-09 18:45 rox

    一直知道異常過多,會有問題,但不知道為什么?
    謝謝了!  回復  更多評論   

    # re: Java Exception性能問題 2010-08-12 17:58 啊寶

    我們的系統也是用異常做的控制業務流程的,提到異常就煩  回復  更多評論   

    # re: Java Exception性能問題 2010-08-13 09:08 stone2083

    @啊寶
    控制流程用Exception(try..catch)也好,還是ResultModel(if..else)也罷.只是不同的設計理念而已.
    都可以,都對

    至于提到異常煩,如果是為性能問題.我以為大可不必.
    1.異常的性能沒那么差.在上面的測試中,一個異常的產生,0.02ms而已
    2.大多數的應用,對性能要求并非很高
    3.引起性能瓶頸的,往往是不合理的設計,錯誤的使用同步等業務代碼產生的.
    4.實在不行,就是用改進后的異常

    如果再不行,那么只好拋棄業務異常吧
    如果再不行,那么只好拋棄java改用c等語言吧.

    選擇一種語言也好,選擇一種設計也罷,只是為了更好的處理需求而已.

    至于上文,只是為了描述異常的本質.在了解原理的基礎上,讓業務異常的使用不出現性能的浪費而已.
    絕不是表明,異常性能真得對系統產生了影響 :)
      回復  更多評論   

    # re: Java Exception性能問題 2012-09-20 12:18 alswl

    感謝分析,知道為什么異常引起瓶頸了  回復  更多評論   

    # re: Java Exception性能問題 2012-11-27 17:07 fillInStackTrace的 synchronized 無影響

    測試 new Exception() 時,我多測試一個自定義異常,在其中覆寫,fillInStackTrace,但是,帶著 synchronized 修飾符。運行結果顯示:是否帶有 synchronized 修飾符 對耗時沒有任何影響

    win7
    java version "1.6.0_37"
    Java(TM) SE Runtime Environment (build 1.6.0_37-b06)
    Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01, mixed mode)  回復  更多評論   

    # re: Java Exception性能問題 2012-11-27 17:10 fillInStackTrace的 synchronized 無影響

    https://blogs.oracle.com/jrose/entry/longjumps_considered_inexpensive
    這里面說:主要是 creation 浪費時間,throw 不浪費時間(我覺得,這和你的 try-catch, if-else 測試是吻合的)  回復  更多評論   

    # re: Java Exception性能問題 2012-11-28 11:27 stone2083

    @fillInStackTrace的 synchronized 無影響
    throw對于jvm來說,只是一條athrow指令,將異常壓棧出棧而已。所以開銷非常小。

    至于synchronized測試,能否將你的測試代碼貼一下(是在多線程條件下測試的嗎?)
    我之前的數據沒有了,這次新做了測試,差異還是很大的。
    20個線程下:
    Opt Take Time: 1372
    Gen Take Time: 36510

    50個線程下:
    Opt Take Time: 3906
    Gen Take Time: 88240

    mac jdk 1.6 64bit  回復  更多評論   

    # re: Java Exception性能問題 2013-01-20 01:17 weipeng

    @stone2083
    我這邊單線程測試,不使用同步的已經比使用同步的快了約3倍。所以同步還是有影響的,畢竟有monitor的進入和退出。

    ps:
    業務異常我也比較喜歡,但是就是有篡改失敗的結果的可能,比如偷懶catch了Exception。
    try {
    // member 不存在
    throw new BusinessException(Xxx);

    // offer不存在
    throw new BusinessException(Yyy);

    // 調用一些業務接口,如 xxx.create(param); 但是它throws 自定義異常
    } catch (Exception) { // 對捕獲到業務接口的異常,想返回系統錯誤
    throw new BusinessException("SystemError...");
    }

    這樣就造成了,如果member不存在,那么就返回系統錯誤了,而不是原來想的member不存在的業務編碼了。

    所以,我覺得如果使用業務異常來做控制,那么調用端,在調用時可以避免對錯誤的判空,而且層次感通過catch來的比較養眼。但是有點劣勢的就是要求在實現內部對于異常的處理要求非常統一。  回復  更多評論   

    # re: Java Exception性能問題 2013-01-21 11:03 stone2083

    @weipeng
    確實,存在如你所說的問題。
    但是問題的本質還在于使用者,忽視業務分支邏輯導致的。
    即便使用If Else,也存在類似的問題(只是發生普遍性相對會?。?,如:
    if(result.isMemberNotFound || result.isOfferNotFound) {
    System.out.println("Sys Error.");
    }

    我現在到不糾結使用哪種形式,唯一要求是:同個項目內部要保持風格統一,并且規范使用。  回復  更多評論   

    # re: Java Exception性能問題[未登錄] 2013-12-17 16:07 呵呵

    異常不應該被用來做業務,即使通過改寫也一樣,同樣需要創建一個實例,new一個實例同樣是不小的開銷,虛擬機還要對他進行回收。如果異常情況反復出現,同樣會大量消耗系統資源。異常當然是用來做異常的,異常字面意思就是系統運行正常的時候不應該出現的,如果出現了才被認為是異常。所以異常應該是很少出現的,由于使用try-catch減少了if判斷,提升了性能。所以異常是為了提升性能的,不是用來消耗性能的。  回復  更多評論   

    # re: Java Exception性能問題 2014-08-15 16:08 wyc

    classloader不是就是用異常來處理業務的么,自己定義的類肯定要通過異常截獲來加載  回復  更多評論   

    # re: Java Exception性能問題 2014-11-24 17:50 zuidaima

    java 異常相關demo源代碼下載:http://zuidaima.com/share/k%E5%BC%82%E5%B8%B8-p1-s1.htm  回復  更多評論   

    # re: Java Exception性能問題[未登錄] 2015-12-16 14:48 小明

    沒有并發,測試準么?  回復  更多評論   

    # re: Java Exception性能問題 2015-12-16 19:18 stone2083

    @小明
    private static final ExecutorService POOL = Executors.newFixedThreadPool(30);  回復  更多評論   

    主站蜘蛛池模板: www亚洲精品久久久乳| 国产免费一区二区三区在线观看| 成人免费午夜无码视频| 亚洲中文字幕久久精品无码VA| 午夜成人免费视频| 67194在线午夜亚洲| 国产伦一区二区三区免费| 久青草视频在线观看免费| 久久亚洲sm情趣捆绑调教| 国产美女精品视频免费观看| aa毛片免费全部播放完整| 国产精品手机在线亚洲| 亚洲国产精品第一区二区| 在线播放免费播放av片| 国产免费不卡视频| 一级做a爱片特黄在线观看免费看| 久久av无码专区亚洲av桃花岛| 国产免费一区二区三区VR| 国产精品视频免费一区二区| 视频免费在线观看| 亚洲精品无码久久久久秋霞| 好看的亚洲黄色经典| 四虎影视免费在线| 一个人在线观看视频免费| 4399好看日本在线电影免费| 一级毛片成人免费看免费不卡 | 国产成人精品免费大全| 亚洲国产亚洲片在线观看播放 | 国产青草亚洲香蕉精品久久| 在线观看亚洲AV日韩AV| 狠狠色香婷婷久久亚洲精品| 亚洲三级中文字幕| 亚洲av无码成h人动漫无遮挡| 精品国产麻豆免费网站| 免费看的黄色大片| 凹凸精品视频分类国产品免费| 免费欧洲毛片A级视频无风险| 又黄又大又爽免费视频| 成人毛片视频免费网站观看| 99视频在线免费看| GOGOGO高清免费看韩国|