try-catch和throws Exception在性能方面有什么区别?

时间:2017-12-28 04:48:50

标签: java performance exception

我知道在使用方面存在差异。但我想讨论性能差异。

顺便说一下," Effective Java":

中有一句话
  

将代码置于try-catch块中会禁止现有JVM实现可能执行的某些优化。

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

3 个答案:

答案 0 :(得分:2)

为什么不尝试代码?

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子句确实需要一些时间。

答案 1 :(得分:1)

从论文Optimizing Java Programs in the Presence of Exceptions

开始
  

Java语言规范要求异常准确,   这意味着:

     
      
  1. 抛出异常时,相应异常处理程序入口处的程序状态observable必须与   原计划;和
  2.   
  3. 异常必须按原始(未优化)程序指定的顺序抛出。
  4.         

    为了满足精确的异常要求,Java编译器禁用   许多重要的优化可能会引发一些指令   例外...这妨碍了广泛的程序优化,例如   指令调度,指令选择,循环变换,   和并行化。

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

另一方面,

throws允许异常传播。虽然这对性能可能更好,但未捕获的异常可能会使线程,整个应用程序崩溃,甚至杀死JVM本身!

如何决定何时使用try-catch

我认为正确性和可靠性问题比这里的表现更重要。但是如果你只考虑了性能,this link中的作者已经在存在异常的情况下对java代码进行了广泛的基准测试。他们最基本的结论是,如果你使用异常来处理真正特殊情况(不是作为程序流控制的手段),那么性能就不会受到太大影响。

答案 2 :(得分:0)

我使用了这段代码

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毫秒

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

我没有考虑在catch语句中使用代码运行,如果你正在使用try和catch语句,那么你需要运行该代码并且你不会抛出异常。