即使从未引发异常,使用 try-catch 块是否成本高昂?

2022-08-31 06:05:42

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

我发现堆栈溢出问题/答案为什么尝试块很昂贵?,但它是针对.NET的。


答案 1

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


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

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


答案 2

让我们来衡量一下,好吗?

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

在我的计算机上,这将打印如下内容:

try     0.598 ns
no try  0.601 ns

至少在这个微不足道的示例中,try 语句对性能没有可衡量的影响。随意测量更复杂的。

一般来说,我建议在代码中有实际性能问题的证据之前,不要担心语言构造的性能成本。或者正如唐纳德·高德纳所说:“过早的优化是万恶之源”。


推荐