即使从不抛出exception,使用try-catch块也很昂贵吗?

我们知道,发现exception是很昂贵的。 但是,即使从不抛出exception,在Java中使用try-catch块也是昂贵的吗?

我发现堆栈溢出问题/答案为什么尝试块昂贵? ,但它是为.NET 。

try几乎没有任何费用。 代码的元数据不是在运行时设置try的工作,而是在编译时构造的,这样当抛出一个exception时,它现在执行一个相对昂贵的操作来检查堆栈,看看是否存在任何try块赶上这个例外。 从外行的angular度来看, try也可以免费。 实际上,抛出exception会让你付出代价 – 但除非抛出成千上万的exception,否则你仍然不会注意到成本。


try有一些相关的小成本。 Java不能对try块中的代码进行一些优化,否则它会这样做。 例如,Java通常会在一个方法中重新排列指令以使其运行得更快 – 但是Java也需要保证,如果抛出exception,那么方法的执行就会像在源代码中写入的语句一样被执行以达到一些线。

因为在一个try块中,可以抛出一个exception(在try块的任何一行中!)一些exception是asynchronous抛出的,比如通过调用一个线程的stop (这已被废弃),甚至OutOfMemoryError几乎可以在任何地方发生)然而它可以被捕获并且代码继续以相同的方法执行,所以可以进行优化的理由比较困难,因此它们不太可能发生。 (有人必须编程编译器来做这些事情,推理和保证正确性等等。对于某些意思是“特殊”的东西来说,这将是一个很大的痛苦)但是在实践中,你将不会注意到这样的事情。

我们来衡量一下吧?

 public abstract class Benchmark { final String name; public Benchmark(String name) { this.name = name; } abstract int run(int iterations) throws Throwable; private BigDecimal time() { try { int nextI = 1; int i; long duration; do { i = nextI; long start = System.nanoTime(); run(i); duration = System.nanoTime() - start; nextI = (i << 1) | 1; } while (duration < 100000000 && nextI > 0); return new BigDecimal((duration) * 1000 / i).movePointLeft(3); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public String toString() { return name + "\t" + time() + " ns"; } public static void main(String[] args) throws Exception { Benchmark[] benchmarks = { new Benchmark("try") { @Override int run(int iterations) throws Throwable { int x = 0; for (int i = 0; i < iterations; i++) { try { x += i; } catch (Exception e) { e.printStackTrace(); } } return x; } }, new Benchmark("no try") { @Override int run(int iterations) throws Throwable { int x = 0; for (int i = 0; i < iterations; i++) { x += i; } return x; } } }; for (Benchmark bm : benchmarks) { System.out.println(bm); } } } 

在我的电脑上,打印出如下所示的内容:

 try 0.598 ns no try 0.601 ns 

至less在这个微不足道的例子中,try语句对性能没有可衡量的影响。 随意测量更复杂的。

一般来说,我build议您不要担心语言结构的性能成本,除非您的代码中存在实际性能问题的证据。 或者正如唐纳德·克努斯(Donald Knuth) 所说 :“不成熟的优化是万恶之源”。

try / catch可能会对性能产生一些影响。 这是因为它阻止了JVM做一些优化。 Joshua Bloch在“Effective Java”中说道:

将代码放置在try-catch块内禁止某些现代JVM实现可能执行的优化。

是的,正如其他人所说的,一个try块会阻止围绕它的{}字符进行一些优化。 特别是,优化器必须假定在块内的任何一点都可能发生exception,所以不能保证执行语句。

例如:

  try { int x = a + b * c * d; other stuff; } catch (something) { .... } int y = a + b * c * d; use y somehow; 

如果没有try ,计算分配给x的值可以保存为“公共子expression式”并重新用于分配给y 。 但由于try不能保证第一个expression式是经过评估的,所以expression式必须重新计算。 这在“直线”代码中通常不是什么大问题,但是在循环中可能很重要。

但是,应该指出的是,这仅适用于JITCed代码。 javac只做一些优化,而字节码解释器进入/离开try块的成本是零。 (没有生成字节码来标记块边界。)

对于bestsss:

 public class TryFinally { public static void main(String[] argv) throws Throwable { try { throw new Throwable(); } finally { System.out.println("Finally!"); } } } 

输出:

 C:\JavaTools>java TryFinally Finally! Exception in thread "main" java.lang.Throwable at TryFinally.main(TryFinally.java:4) 

javap输出:

 C:\JavaTools>javap -c TryFinally.class Compiled from "TryFinally.java" public class TryFinally { public TryFinally(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]) throws java.lang.Throwable; Code: 0: new #2 // class java/lang/Throwable 3: dup 4: invokespecial #3 // Method java/lang/Throwable."<init>":()V 7: athrow 8: astore_1 9: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 12: ldc #5 // String Finally! 14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 17: aload_1 18: athrow Exception table: from to target type 0 9 8 any } 

没有“GOTO”。

为了理解优化不能执行的原因,理解底层机制是有用的。 我能find的最简洁的例子是在Cmacros中实现的: http : //www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

 #include <stdio.h> #include <setjmp.h> #define TRY do{ jmp_buf ex_buf__; switch( setjmp(ex_buf__) ){ case 0: while(1){ #define CATCH(x) break; case x: #define FINALLY break; } default: #define ETRY } }while(0) #define THROW(x) longjmp(ex_buf__, x) 

编译器通常难以确定跳转是否可以被本地化为X,Y和Z,因此它们跳过了不能保证是安全的优化,但是实现本身是相当轻的。

又一个微基准( 来源 )。

我创build了一个testing,其中我测量了基于exception百分比的try-catch和no-try-catch代码版本。 10%的百分比意味着10%的testing案例被零除。 在一种情况下,它由try-catch块处理,另一个由条件运算符处理。 这是我的结果表格:

 OS: Windows 8 6.2 x64 JVM: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.25-b01 
百分比| 结果(try / if,ns)   
     0%|  88/90   
     1%|  87分之89    
     10%|  97分之86    
     90%|  83分之85   

其中说任何这些情况之间没有显着差异。