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
Is it expensive to use try-catch blocks even if an exception is never thrown?
提问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
try
has almost no expense at all. Instead of doing the work of setting up the try
at 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 try
blocks exist that would catch this exception. From a layman's perspective, try
may 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
如自由。它实际上是抛出使您付出代价的异常 - 但除非您抛出数百或数千个异常,否则您仍然不会注意到成本。
try
has some minor costs associated with it. Java cannot do some optimizations on code in a try
block 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 try
block an exception can be thrown (at any line in the try block! Some exceptions are thrown asynchronously, such as by calling stop
on 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
/catch
may 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 try
block 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 x
could be saved as a "common subexpression" and reused to assign to y
. But because of the try
there 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 try
block. (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 毫秒,这对我来说是相当大的改进。