在Java中,try-catch块应该有多深?
由于我的新工作,我在Java方面做了很多工作,现在我正在深入了解一些小细节。显然,Java代码在某种程度上是关于异常的。我想知道: 调用堆栈对try-catch块的性能影响很大吗?也就是说,我应该避免尝试调用以下函数的函数:。。。太深了在Java中,try-catch块应该有多深?,java,performance,try-catch,Java,Performance,Try Catch,由于我的新工作,我在Java方面做了很多工作,现在我正在深入了解一些小细节。显然,Java代码在某种程度上是关于异常的。我想知道: 调用堆栈对try-catch块的性能影响很大吗?也就是说,我应该避免尝试调用以下函数的函数:。。。太深了 我读到try-catch块只影响异常时的性能。然而,它们泡沫化的程度有关系吗?例外情况是昂贵的。使用时,将创建堆栈跟踪。如果可以检查异常,请执行此操作。不要使用try..catch进行流量控制。无法检查/验证时,请使用try..catch;例如,执行IO操作 当
我读到try-catch块只影响异常时的性能。然而,它们泡沫化的程度有关系吗?例外情况是昂贵的。使用时,将创建堆栈跟踪。如果可以检查异常,请执行此操作。不要使用try..catch进行流量控制。无法检查/验证时,请使用try..catch;例如,执行IO操作
当我看到包含大量try..catch块的代码时,我的直接想法是“这是一个糟糕的设计!”。在调用.printStackTrace或.getStackTrace之前,不会加载堆栈跟踪。
如果在catch块的开始处放置断点,您将注意到异常对象具有空堆栈跟踪 1.-调用堆栈深度不需要担心,Java实时编译器将对代码类方法内联进行优化,以在代码上提供最佳性能 2.-Catch块确实会影响性能,这是因为捕获异常可能意味着JVM中的几个不同操作,如遍历异常表、堆栈展开和常见陷阱(对JIT的优化代码进行去优化)
3.-作为一条建议,不要担心封装代码并最终导致多个方法调用,这将导致巨大的堆栈深度,JVM将进行优化,以便在编译应用程序和运行应用程序时获得最佳性能,因此始终以代码可读性和良好的设计模式为目标,因为这将有助于使代码更易于维护和修改,以防出现某些性能修复。关于catch块,请评估抛出或捕获异常的必要性,如果正在捕获,请尝试捕获最常见的异常,这样可以避免出现大量异常表。让我们来衡量一下,好吗
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));
}
}
}
}
显然,具体结果会因硬件、JVM实现和配置的不同而有所不同。然而,总体格局可能会保持不变
结论:
- try语句本身产生的开销可以忽略不计
- 抛出异常并展开调用堆栈会导致堆栈大小(或要展开的堆栈数量)的线性开销。
- 对于实际应用程序的堆栈大小(假设为100个堆栈帧),该开销约为50微秒,即0.00005秒
- 通过抛出没有堆栈跟踪的异常,可以在一定程度上减少这种开销
- 不要担心try语句的性能
- 不要对频繁发生的信号条件使用异常(例如,每秒超过1000次)李>
- 否则,不要担心抛出异常的性能
- 还有,;-)李>
fillInStackTrace()
调用Throwable
构造函数。但这些方法都是本机的,可能性能很高stackTrace
为空,因为此处使用了延迟初始化。进一步您可以看到,该堆栈跟踪是通过使用其他本机方法创建的,如getStackTraceDepth()
和getStackTraceElement(int)
中的getOurStackTrace()
。
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