Java中的try-catch块应该有多深?

时间:2013-09-04 11:56:44

标签: java performance try-catch

由于我的新工作,我在Java上工作了很多,现在我正在深入研究细节。显然,Java代码在某种程度上与异常有关。我在想:

调用堆栈是否会影响try-catch块的性能?即我应该避免尝试一个函数来调用一个函数,而且函数太深了吗?

我读到try-catch块只影响异常时的性能。然而,它们起泡的重要性是什么?

4 个答案:

答案 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实施和配置而异。但是,一般模式可能保持不变。

结论:

  • try语句本身会产生微不足道的开销。
  • 抛出异常并展开callstack会导致堆栈大小的线性上升(或者展开的堆栈数量)。
    • 对于真实世界应用程序的堆栈大小(让我们假设有100个堆栈帧),这个开销约为50微秒,或0.00005秒。
    • 通过抛出没有堆栈跟踪的异常可以减少开销

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对象具有空堆栈跟踪。