你知道java中的异常抛出和处理是多么昂贵吗?
我们在团队中就异常的实际成本进行了多次讨论.有些人认为通过使用例外而导致的性能损失被高估了.
今天我在我们的软件中找到了以下代码:
private void doSomething() { try { doSomethingElse(); } catch(DidNotWorkException e) { log("A Message"); } goOn(); } private void doSomethingElse() { if(isSoAndSo()) { throw new DidNotWorkException(); } goOnAgain(); }
这与此相比如何表现
private void doSomething() { doSomethingElse(); goOn(); } private void doSomethingElse() { if(isSoAndSo()) { log("A Message"); return; } goOnAgain(); }
我不想讨论代码美学或任何东西,它只是关于运行时行为!你有真实的经验/测量吗?
例外不是免费的......所以它们很贵:-)
Effective Java这本书详细介绍了这一点.
项目39仅在特殊情况下使用例外.
第40项对可恢复条件使用例外
作者发现,异常导致代码调整速度慢了70倍,因为他的机器上的测试用具有特定的VM和OS组合.
抛出异常的最慢部分是填充堆栈跟踪.
如果您预先创建例外并重新使用它,JIT可以将其优化为" 机器级别转到".
所有这一切,除非你的问题中的代码是一个非常紧密的循环,差异将是微不足道的.
关于异常的缓慢部分是构建堆栈跟踪(在构造函数中java.lang.Throwable
),这取决于堆栈深度.投掷本身并不慢.
使用例外来表示失败.然后,性能影响可以忽略不计,堆栈跟踪有助于确定故障原因.
如果您需要控制流的异常(不推荐),并且分析显示异常是瓶颈,那么创建一个覆盖fillInStackTrace()
空实现的Exception子类.或者(或另外)仅实例化一个异常,将其存储在字段中并始终抛出相同的实例.
下面通过在接受的答案中向微基准添加一个简单的方法(虽然有缺陷),演示了没有堆栈跟踪的异常:
public class DidNotWorkException extends Exception { public Throwable fillInStackTrace() { return this; } }
在-server
模式下使用JVM (Windows 7上的版本1.6.0_24)运行它会导致:
Exception:99ms Boolean:12ms Exception:92ms Boolean:11ms
差异很小,在实践中可以忽略不计.
我没有费心去阅读Exceptions但是用你的一些修改过的代码做了一个非常快速的测试我得出结论,Exception情况比布尔情况慢得多.
我得到了以下结果:
Exception:20891ms Boolean:62ms
从这段代码:
public class Test { public static void main(String args[]) { Test t = new Test(); t.testException(); t.testBoolean(); } public void testException() { long start = System.currentTimeMillis(); for(long i = 0; i <= 10000000L; ++i) doSomethingException(); System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms"); } public void testBoolean() { long start = System.currentTimeMillis(); for(long i = 0; i <= 10000000L; ++i) doSomething(); System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms"); } private void doSomethingException() { try { doSomethingElseException(); } catch(DidNotWorkException e) { //Msg } } private void doSomethingElseException() throws DidNotWorkException { if(!isSoAndSo()) { throw new DidNotWorkException(); } } private void doSomething() { if(!doSomethingElse()) ;//Msg } private boolean doSomethingElse() { if(!isSoAndSo()) return false; return true; } private boolean isSoAndSo() { return false; } public class DidNotWorkException extends Exception {} }
我愚蠢地没有充分阅读我的代码并且之前有一个错误(多么令人尴尬),如果有人可以三重检查这段代码我会非常喜欢它,以防万一我老去了.
我的规格是:
编译并在1.5.0_16上运行
Sun JVM
WinXP SP3
英特尔迅驰双核T7200(2.00Ghz,977Mhz)
2.00 GB Ram
在我看来,您应该注意到非异常方法不会在doSomethingElse中给出日志错误,而是返回一个布尔值,以便调用代码可以处理失败.如果有多个区域可能会失败,则可能需要在内部记录错误或抛出异常.