Java异常有多慢?

问题:

问题:Java中的异常处理实际上是慢吗?
传统的智慧,以及大量的Google结果表明,Java中的正常程序流不应该使用异常逻辑。通常给出两个原因,

  1. 它真的很慢 – 甚至比常规代码慢一个数量级(给出的原因有所不同),

  1. 它的凌乱,因为人们期望只有错误被处理在特殊的代码。

这个问题是#1。
例如,this page将Java异常处理描述为“非常慢”,并将缓慢性与创建异常消息字符串相关联 – “这个字符串然后用于创建抛出的异常对象,这不是很快。文章Effective Exception Handling in Java说“这是因为异常处理的对象创建方面的原因,从而使抛出异常本身就慢”。另一个原因是堆栈跟踪生成是减慢它的速度。
我的测试(使用Java 1.6.0_07,32位Linux上的Java HotSpot 10.0)表示异常处理不比常规代码慢。我尝试运行一个执行一些代码的循环中的方法。在方法结束时,我使用一个布尔值来指示是否为returnthrow。这样实际的处理是一样的。我尝试以不同的顺序运行方法,平均我的测试时间,认为可能是JVM升温。在我的所有测试中,投掷速度至少与返回速度一样快,如果不是更快(高达3.1%)。我完全开放我的测试是错误的可能性,但是我没有看到任何代码示例,测试比较的方式,或在过去一两年中显示Java中的异常处理实际上是慢的结果。
什么导致我这个路径是一个API我需要使用,抛出异常作为正常控制逻辑的一部分。我想纠正他们的使用,但现在我可能无法。我是否应该赞美他们的前瞻性思维?
在文章Efficient Java exception handling in just-in-time compilation中,作者建议,即使没有异常被抛出,异常处理程序的存在也足以阻止JIT编译器正确地优化代码,从而减慢它的运行速度。我还没有测试这个理论。

回答:

这取决于如何实现异常。最简单的方法是使用setjmp和longjmp。这意味着CPU的所有寄存器都被写入堆栈(已经需要一些时间),并且可能需要创建一些其他数据…所有这一切已经在try语句中发生。 throw语句需要展开堆栈并恢复所有寄存器的值(以及VM中可能的其他值)。所以尝试和抛出同样慢,这是相当缓慢的,但是如果没有异常抛出,退出try块在大多数情况下都不需要时间(因为所有的东西都放在堆栈里,如果方法存在的话自动清理)。
Sun和其他人认识到,这可能不是最佳的,当然VM在这段时间内变得越来越快。还有另外一种方式来实现异常,这使得尝试本身闪电般快速(实际上没有任何事情发生,一般来说 – 所有需要发生的事情都已经在虚拟机加载该类的时候完成了),并且使得抛出不太慢。我不知道哪个JVM使用这种新的更好的技术…
…但是您是用Java编写的,因此您的代码稍后只能在一个特定系统上的一个JVM上运行?既然如果它可能运行在任何其他平台或任何其他JVM版本(可能是任何其他供应商),谁说他们也使用快速实现?快速的比较慢,而且在所有系统上都不容易。你想保持便携?那么不要依赖异常快。
它也在尝试块中做了很大的改变。如果您打开一个try块,从来没有调用这个try块中的任何方法,try块将是超快的,因为JIT可以实际上像一个简单的goto一样抛出。它不需要保存堆栈状态,也不需要展开堆栈,如果抛出异常(它只需要跳转到catch处理程序)。但是,这不是你通常做的。通常你打开一个try块,然后调用一个可能会抛出异常的方法,对吧?即使你只是在你的方法中使用try块,这样做什么样的方法呢,不会调用任何其他方法?它只是计算一个数字?那么你需要什么例外呢?有更优雅的方式来规范程序流程。除了简单的数学之外,你还需要调用一个外部方法,这已经破坏了本地try块的优点。
请参阅以下测试代码:

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() ); } } [/code] 结果: [code lang="python"] method1 took 972 ms, result was 2 method2 took 1003 ms, result was 2 method3 took 66716 ms, result was 2 [/code] 尝试块的减速度太小,无法排除诸如后台进程等混杂因素。但是这个拦截块杀死了所有的东西,使其慢了66倍! 正如我所说,如果你把try / catch放在同一个方法(method3)中,结果不会那么糟糕,但这是一个我不会依赖的特殊JIT优化。即使使用这种优化,投掷仍然相当缓慢。所以我不知道你在这里做什么,但是绝对比使用try / catch / throw更好的方法。     Code问答: http://codewenda.com/topics/python/
Stackoverflow: How slow are Java exceptions?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

+ 45 = 49