Java 即使从未抛出异常,使用 try-catch 块是否昂贵?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16451777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:59:13  来源:igfitidea点击:

Is it expensive to use try-catch blocks even if an exception is never thrown?

javaperformancetry-catch

提问by jsedano

We know that it is expensive to catch exceptions. But, is it also expensive to use a try-catch block in Java even if an exception is never thrown?

我们知道捕获异常的代价很高。但是,即使从未抛出异常,在 Java 中使用 try-catch 块也很昂贵吗?

I found the Stack Overflow question/answer Why are try blocks expensive?, but it is for .NET.

我找到了 Stack Overflow 问题/答案为什么 try 块很贵?,但它适用于.NET

采纳答案by Patashu

tryhas almost no expense at all. Instead of doing the work of setting up the tryat runtime, the code's metadata is structured at compile time such that when an exception is thrown, it now does a relatively expensive operation of walking up the stack and seeing if any tryblocks exist that would catch this exception. From a layman's perspective, trymay as well be free. It's actually throwing the exception that costs you - but unless you're throwing hundreds or thousands of exceptions, you still won't notice the cost.

try几乎没有任何费用。try代码的元数据不是在运行时进行设置的工作,而是在编译时进行结构化,这样当抛出异常时,它现在会执行相对昂贵的操作:遍历堆栈并查看是否try存在可以捕获该异常的块例外。从外行的角度来说,还不try如自由。它实际上是抛出使您付出代价的异常 - 但除非您抛出数百或数千个异常,否则您仍然不会注意到成本。



tryhas some minor costs associated with it. Java cannot do some optimizations on code in a tryblock that it would otherwise do. For example, Java will often re-arrange instructions in a method to make it run faster - but Java also needs to guarantee that if an exception is thrown, the method's execution is observed as though its statements, as written in the source code, executed in order up to some line.

try有一些与之相关的小成本。Java 无法对try块中的代码进行一些优化,否则它会这样做。例如,Java 通常会重新安排方法中的指令以使其运行得更快——但 Java 还需要保证如果抛出异常,则观察该方法的执行,就好像它的语句,如源代码中所写,被执行为了达到某些行。

Because in a tryblock an exception can be thrown (at any line in the try block! Some exceptions are thrown asynchronously, such as by calling stopon a Thread (which is deprecated), and even besides that OutOfMemoryError can happen almost anywhere) and yet it can be caught and code continue to execute afterwards in the same method, it is more difficult to reason about optimizations that can be made, so they are less likely to happen. (Someone would have to program the compiler to do them, reason about and guarantee correctness, etc. It'd be a big pain for something meant to be 'exceptional') But again, in practice you won't notice things like this.

因为在try块中可以抛出异常(在 try 块中的任何行!一些异常是异步抛出的,例如通过调用stop线程(已弃用),甚至除了 OutOfMemoryError 几乎可以在任何地方发生),但它可以被捕获,然后代码在相同的方法中继续执行,更难以推理可以进行的优化,因此它们不太可能发生。(有人必须编写编译器来执行它们,推理并保证正确性等。对于意味着“异常”的东西来说,这将是一个很大的痛苦)但是同样,在实践中你不会注意到这样的事情。

回答by Evgeniy Dorofeev

try/catchmay have some impact on performance. This is because it prevents JVM from doing some optimizations. Joshua Bloch, in "Effective Java," said the following:

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

? Placing code inside a try-catch block inhibits certain optimizations that modern JVM implementations might otherwise perform.

? 将代码放在 try-catch 块中会抑制现代 JVM 实现可能执行的某些优化。

回答by Hot Licks

Yep, as the others have said, a tryblock inhibits some optimizations across the {}characters surrounding it. In particular, the optimizer must assume that an exception could occur at any point within the block, so there's no assurance that statements get executed.

是的,正如其他人所说,一个try块会禁止{}对它周围的字符进行一些优化。特别是,优化器必须假设在块内的任何点都可能发生异常,因此不能保证语句得到执行。

For example:

例如:

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

Without the try, the value calculated to assign to xcould be saved as a "common subexpression" and reused to assign to y. But because of the trythere is no assurance that the first expression was ever evaluated, so the expression must be recomputed. This isn't usually a big deal in "straight-line" code, but can be significant in a loop.

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

It should be noted, however, that this applies ONLY to JITCed code. javac does only a piddling amount of optimization, and there is zero cost to the bytecode interpreter to enter/leave a tryblock. (There are no bytecodes generated to mark the block boundaries.)

然而,应该注意的是,这仅适用于 JITCed 代码。javac 只做了微不足道的优化,字节码解释器进入/离开try块的成本为零。(没有生成字节码来标记块边界。)

And for bestsss:

对于最好的:

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

Output:

输出:

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

javap output:

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
}

No "GOTO".

没有“转到”。

回答by meriton

Let's measure it, shall we?

让我们测量一下,好吗?

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);
        }
    }
}

On my computer, this prints something like:

在我的电脑上,这会打印如下内容:

try     0.598 ns
no try  0.601 ns

At least in this trivial example, the try statement had no measurable impact on performance. Feel free to measure more complex ones.

至少在这个简单的例子中,try 语句对性能没有可衡量的影响。随意测量更复杂的。

Generally speaking, I recommend not to worry about the performance cost of language constructs until you have evidence of an actual performance problem in your code. Or as Donald Knuth putit: "premature optimization is the root of all evil".

一般来说,我建议不要担心语言结构的性能成本,直到您有证据表明代码中存在实际性能问题。或者正如 Donald Knuth所说:“过早的优化是万恶之源”。

回答by technosaurus

To understand why the optimizations cannot be performed, It is useful to understand the underlying mechanisms. The most succinct example I could find was implemented in C macros at: http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

要了解无法执行优化的原因,了解底层机制很有用。我能找到的最简洁的例子是在 C 宏中实现的: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)

Compilers often have difficulty determining if a jump can be localized to X, Y and Z so they skip optimizations that they can't guarantee to be safe, but the implementation itself is rather light.

编译器通常难以确定是否可以将跳转定位到 X、Y 和 Z,因此他们会跳过无法保证安全的优化,但实现本身相当轻巧。

回答by Andrey Chaschev

Yet another microbenchmark (source).

另一个微基准测试(来源)。

I created a test in which I measure try-catch and no-try-catch code version based on an exception percentage. 10% percentage means that 10% of the test cases had division by zero cases. In one situation it is handled by a try-catch block, in the other by a conditional operator. Here is my results table:

我创建了一个测试,其中我根据异常百分比测量 try-catch 和 no-try-catch 代码版本。10% 的百分比意味着 10% 的测试用例被零个用例除。在一种情况下,它由 try-catch 块处理,在另一种情况下由条件运算符处理。这是我的结果表:

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

Which says that there is no significant difference between any of these cases.

也就是说,这些案例之间没有显着差异。

回答by Mateusz Kaflowski

I have found catching NullPointException quite expensive. For 1.2k operations the time was 200ms and 12ms when I handeled it the same way with if(object==null)which was pretty improvement for me.

我发现捕获 NullPointException 非常昂贵。对于 1.2k 操作,当我以相同的方式处理if(object==null)它时,时间是 200 毫秒和 12 毫秒,这对我来说是相当大的改进。