在性能方面,尝试键入和引发异常有什么区别



我知道用法有差异。但是我想讨论性能差异。

顺便说一句,"有效java"中有一个句子:

将代码放入trycatch块中抑制了现代JVM实现可能执行的某些优化。

因此,如果有一个方法抛出异常,是否意味着这种优化不会应用于此方法?

为什么不尝试代码?

public class ExPerformanceTest {
    private int someVar = 0;
    private int iterations = 100000000;
    @Test
    public void throwTest() throws Exception {
        long t;
        t = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            throwingMethod();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Throw Test took " + t + " ms");
    }
    @Test
    public void tryCatchTest() throws Exception {
        long t;
        t = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            tryCatchMethod();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Try-Catch Test took " + t + " ms");
    }
    @Test
    public void anotherTryCatchTest() throws Exception {
        long t;
        t = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            tryCatchMethodThatNeverEverThrows();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Try-Catch That Never Throws Test took " + t + " ms");
    }
    private void throwingMethod() throws Exception {
        // do some stuff here
        someVar++;
        willNeverThrow();
    }
    private void tryCatchMethod() {
        try {
            // do some stuff here
            someVar++;
            willNeverThrow();
        } catch (Exception e) {
            System.out.println("You shouldn't see this ever");
        }
    }
    private void tryCatchMethodThatNeverEverThrows() {
        try {
            // do some stuff here
            someVar++;
        } catch (Exception e) {
            System.out.println("You shouldn't see this ever");
        }
    }
    private void willNeverThrow() throws Exception {
        if (someVar == -1) {
            throw new RuntimeException("Shouldn't happen");
        }
    }
}

给出了相当预期的数字:

运行1

Try-Catch That Never Throws Test took 36 ms
Throw Test took 139 ms
Try-Catch Test took 160 ms

运行2

Try-Catch That Never Throws Test took 26 ms
Throw Test took 109 ms
Try-Catch Test took 113 ms

运行3

Try-Catch That Never Throws Test took 32 ms
Throw Test took 137 ms
Try-Catch Test took 194 ms

显然,JVM发现tryCatchMethodThatNeverEverThrows不需要有效的catch部分并进行了优化,因此该方法执行时间比其他方法少几次。

在其他情况下,使用catch子句确实需要一些时间。

从纸张中,在存在的情况下优化Java程序:

Java语言规范要求例外是精确的, 这意味着:

  1. 当抛出异常时,在相应异常处理程序进入时可观察到的程序状态必须与 原始程序;和
  2. 异常必须以与原始程序(不优化)程序指定的顺序相同。

为了满足确切的例外要求,Java编译器禁用 跨说明的许多重要优化,可能会抛出 例外...这妨碍了广泛的程序优化,例如 说明计划,指令选择,循环转换, 和并行化。

在存在try-catch块的情况下,JVM保持异常表。对于每个新的捕获块,编译器将向异常表添加一个条目。如果发生异常,JVM需要遍历表中的每个条目,并检查异常表中是否定义了异常(这也需要类型分析)。结果是一个更复杂的控制结构,反过来将大大阻碍编译器的优化。

另一方面,

throws允许例外传播。尽管这可能对性能更好,但是未被发现的例外可能会崩溃,整个应用程序,甚至杀死JVM本身!

如何决定何时使用try-catch

我认为正确性和可靠性问题在这里的表现更重要。但是,如果您只有表现,则此链接中的作者在有例外的情况下为Java代码做了广泛的基准标记。他们最基本的结论是,如果您对真正的例外情况使用异常(不是作为程序流控制的手段),则不会有太多的性能。

我使用了此代码

public class Test {
static int value;
public void throwsTest(int i) throws Exception {
    value = ((value + i) / i) << 1;
    // if ((i & 0xFFFFFFF) == 1000000000) {
    // }
    if ((i & 0x1) == 1) {
        throw new Exception();
    }
}
public void tryAndCatchTest(int i) {
    value = ((value + i) / i) << 1;
    try {
        // if ((i & 0xFFFFFFF) == 1000000000) {
        // }
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    } catch (Exception e) {
    }
}
public static void main(String[] args) throws Exception {
    int i;
    long l;
    Test t = new Test();
    l = System.currentTimeMillis();
    value = 0;
    for (i = 1; i < 10000000; i++) {
        try {
            t.throwsTest(i);
        } catch (Exception e) {
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println("Throws: " + l + " ms");
    i = 0;
    l = 0;
    l = System.currentTimeMillis();
    value = 0;
    for (i = 1; i < 10000000; i++) {
        try {
            t.tryAndCatchTest(i);
        } catch (Exception e) {
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println("Try and catch: " + l + " ms");
}

}

测试后,有时会随着投掷而更快,有时会尝试捕捉。我尝试了丢弃错误并只是处理语句。因此,总之,它们都同样有效。

示例结果与投掷例外:投掷:6802毫秒尝试捕获:6586 MS

示例结果没有:投掷:68毫秒尝试捕获:72 ms

我没有在捕获语句中使用代码运行,如果您使用的是尝试捕获语句,则无论如何都需要运行该代码,并且不会抛出异常。

<</p>

最新更新