由于我的新工作,我在Java上工作了很多,现在我正在深入研究细节。显然,Java代码在某种程度上与异常有关。我在想:
调用堆栈是否会影响try-catch块的性能?即我应该避免尝试一个函数来调用一个函数,而且函数太深了吗?
我读到try-catch块只影响异常时的性能。然而,它们起泡的重要性是什么?
答案 0 :(得分:2)
让我们衡量一下吧,我们呢?
package tools.bench;
import java.math.BigDecimal;
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 < 1000000000 && 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";
}
enum ExceptionStrategy {
none {
@Override void run() {
// do nothing
}
},
normal {
@Override void run() {
throw new RuntimeException();
}
},
withoutStackTrace {
@Override void run() {
throw new RuntimeException() {
public synchronized Throwable fillInStackTrace() {
return this;
};
};
}
};
abstract void run();
}
private static Benchmark tryBenchmark(final int depth, final ExceptionStrategy strat) {
return new Benchmark("try, depth = " + depth + ", " + strat) {
@Override int run(int iterations) {
int x = 0;
for (int i = 1; i < iterations; i++) {
try {
x += recurseAndThrow(depth);
} catch (Exception e) {
x++;
}
}
return x;
}
private int recurseAndThrow(int i) {
if (i > 0) {
return recurseAndThrow(i - 1) + 1;
} else {
strat.run();
return 0;
}
}
};
}
public static void main(String[] args) throws Exception {
int[] depths = {1, 10, 100, 1000, 10000};
for (int depth : depths) {
for (ExceptionStrategy strat : ExceptionStrategy.values()) {
System.out.println(tryBenchmark(depth, strat));
}
}
}
}
在我的(相当陈旧的)笔记本上,打印出来:
try, depth = 1, none 5.153 ns
try, depth = 1, normal 3374.113 ns
try, depth = 1, withoutStackTrace 602.570 ns
try, depth = 10, none 59.019 ns
try, depth = 10, normal 9064.392 ns
try, depth = 10, withoutStackTrace 3528.987 ns
try, depth = 100, none 604.828 ns
try, depth = 100, normal 49387.143 ns
try, depth = 100, withoutStackTrace 27968.674 ns
try, depth = 1000, none 5388.270 ns
try, depth = 1000, normal 457158.668 ns
try, depth = 1000, withoutStackTrace 271881.336 ns
try, depth = 10000, none 69793.242 ns
try, depth = 10000, normal 2895133.943 ns
try, depth = 10000, withoutStackTrace 2728533.381 ns
显然,具体结果会因您的硬件,JVM实施和配置而异。但是,一般模式可能保持不变。
结论:
Recommendatations:
答案 1 :(得分:1)
例外是昂贵的。使用时,会创建堆栈跟踪。如果您可以检查异常,请执行此操作。不要使用 try..catch 进行流量控制。如果无法检查/验证,请使用 try..catch ;一个例子是进行IO操作。
当我看到包含大量try..catch块的代码时,我的想法是“这是一个糟糕的设计!”。
答案 2 :(得分:0)
1.-调用堆栈深度是您不应该担心的,Java Just In Time Compiler将对您的代码进行优化,如方法内联,以便为您的代码提供最佳性能。
2.- Catch块确实会影响性能,这是因为捕获异常可能意味着JVM中的几个不同的操作,如通过异常表,堆栈展开和常见陷阱(取消优化JIT的优化代码)。
3.-作为一条建议,不要担心封装代码并最终导致几个方法调用,这些调用将导致巨大的堆栈深度,JVM将进行优化以在应用程序中获得最佳性能编译时,它运行时总是以编码可读性和良好的设计模式为目标,因为这将有助于使代码更容易维护,并且在某些性能修复必须启动时更容易修改。关于catch块,评估它的必要性是抛出或捕获异常,如果你正在捕捉尝试捕获最常见的异常,这样你就可以避免大量异常表。
答案 3 :(得分:-1)
在调用.printStackTrace或.getStackTrace之前,不会加载堆栈跟踪。 如果在catch块的开头放置断点,您会注意到Exception对象具有空堆栈跟踪。