Java Exception性能问题

背景: 大学里学java,老师口口声声,言之凿凿,告诫我们,Java千万别用异常控制业务流程,只有系统级别的问题,才能使用异常; (当时,我们都不懂为什么不能用异常,只知道老师这么说,我们就这么做,考试才不会错 :) ) 公司里,有两派.异常拥护者说,使用业务异常,代码逻辑更清晰,更OOP;反之者说,使用异常,性能非常糟糕; (当然,我是拥护者) 论坛上,争论得更多,仁者见仁智者见智,口水很多; (我不发表意见,其实怎么用,真的都可以) 那么,为什么反对异常呢?貌似大多数人的原因都只有一个:性能差! 使用异常性能真的差吗? 是的! 是什么原因,导致性能问题呢? 那么请看下文... 根本原因在于: 异常基类Throwable.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 *
  3  * xen虚拟机,5.5G内存;8核CPU
  4  * LOOP = 10000000
  5  * THREADS = 10
  6  * o:       45284 
  7  * e:       205482 
  8  * exte:    16731
  9  * 
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 newObjectTimes = new ArrayList (THREADS); 21 private static final List newExceptionTimes = new ArrayList (THREADS); 22 private static final List newExtExceptionTimes = new ArrayList (THREADS); 23 24 private static final ExecutorService POOL = Executors.newFixedThreadPool(30); 25 26 public static void main(String[] args) throws Exception { 27 List > all = new ArrayList >(); 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 > tasks(Callable c) { 42 List > list = new ArrayList >(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 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 { 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 { 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 { 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 *
151      * 自定义改进的Exception对象 覆写了 fillInStackTrace方法
152      * 1. 不填充stack
153      * 2. 取消同步
154      * 
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 *
  3  * xen虚拟机,5.5G内存;8核CPU
  4  * LOOP = 100000000
  5  * THREADS = 20
  6  * 
  7  * tc:  101412
  8  * ie:  100749
  9  * 
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 tryCatchTimes = new ArrayList (THREADS); 19 private static final List ifElseTimes = new ArrayList (THREADS); 20 21 private static final ExecutorService POOL = Executors.newFixedThreadPool(40); 22 23 public static void main(String[] args) throws Exception { 24 List > all = new ArrayList >(); 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 > tasks(Callable c) { 37 List > list = new ArrayList >(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 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 { 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 { 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 } /** *
 * xen虚拟机,5.5G内存;8核CPU
 * LOOP = 10000000
 * THREADS = 10
 * o:       45284 
 * e:       205482 
 * exte:    16731
 * 
* * k * * @author li.jinl 2010-7-9 上午09:16:14 */ public class NewExceptionTester { private static final int LOOP = 10000000; // 单次循环数量 private static final int THREADS = 10; // 并发线程数量 private static final List newObjectTimes = new ArrayList (THREADS); private static final List newExceptionTimes = new ArrayList (THREADS); private static final List newExtExceptionTimes = new ArrayList (THREADS); private static final ExecutorService POOL = Executors.newFixedThreadPool(30); public static void main(String[] args) throws Exception { List > all = new ArrayList >(); all.addAll(tasks(new NewObject())); all.addAll(tasks(new NewException())); all.addAll(tasks(new NewExtException())); POOL.invokeAll(all); System.out.println("o:/t/t" + total(newObjectTimes)); System.out.println("e:/t/t" + total(newExceptionTimes)); System.out.println("exte:/t/t" + total(newExtExceptionTimes)); POOL.shutdown(); } private static List > tasks(Callable c) { List > list = new ArrayList >(THREADS); for (int i = 0; i < THREADS; i++) { list.add(c); } return list; } private static long total(List list) { long sum = 0; for (Long v : list) { sum += v; } return sum; } public static class NewObject implements Callable { @Override public Boolean call() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < LOOP; i++) { new CustomObject(""); } newObjectTimes.add(System.currentTimeMillis() - start); return true; } } public static class NewException implements Callable { @Override public Boolean call() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < LOOP; i++) { new CustomException(""); } newExceptionTimes.add(System.currentTimeMillis() - start); return true; } } public static class NewExtException implements Callable { @Override public Boolean call() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < LOOP; i++) { new ExtCustomException(""); } newExtExceptionTimes.add(System.currentTimeMillis() - start); return true; } } /** * 自定义java对象. * * @author li.jinl 2010-7-9 上午11:28:27 */ public static class CustomObject extends HashMap { private static final long serialVersionUID = 5176739397156548105L; private String message; public CustomObject(String message){ this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } /** * 自定义普通的Exception对象 * * @author li.jinl 2010-7-9 上午11:28:58 */ public static class CustomException extends Exception { private static final long serialVersionUID = -6879298763723247455L; private String message; public CustomException(String message){ this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } /** *
     * 自定义改进的Exception对象 覆写了 fillInStackTrace方法
     * 1. 不填充stack
     * 2. 取消同步
     * 
* * @author li.jinl 2010-7-9 上午11:29:12 */ public static class ExtCustomException extends Exception { private static final long serialVersionUID = -6879298763723247455L; private String message; public ExtCustomException(String message){ this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public Throwable fillInStackTrace() { return this; } } 结论: 1。Exception的性能是差,原因在于ThrowablefillInStackTrace()方法 2. 可以通过改写业务异常基类的方法,提升性能 3。try...catch和if...else的性能开销在同一数量级 4。至于是否使用异常进行业务逻辑的控制,主要看代码风格.(我个人挺喜欢业务异常的) 备注: 以上测试比较简单,写得也比较急.此文也写得比较急(工作时间偷偷写).如果分析不到位的地方,请指出.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值