即使从不抛出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
其中说任何这些情况之间没有显着差异。