为什么在不抛出任何异常的情况下处理try-catch根本不会减慢程序的速度?



今天我意识到一件对我来说似乎很奇怪的事情:我注意到当我只是做

try {
    doSomething();
} catch (Exception e) {
}

并不比我直接写

doSomething();

所以我运行了一个测试并写下了一些快速代码来证明我所看到的,代码基本上只是在一个名为doSomething()的函数上循环了很多次,一次没有,一次有try-catch。下面是它的代码,如果你想自己测试的话:

public class Main {
private static final long LOOPS = 1000000L;
public static final void main(String[] args)
{
    System.out.println("Loop without try catch: "+loopWithoutTryCatch(LOOPS));
    System.out.println("Loop with try catch: "+loopWithTryCatch(LOOPS));
}
public static long loopWithoutTryCatch(long loops)
{
    long startTime = System.currentTimeMillis();
    for (long i = 0L; i < loops; i++)
    {
        doSomething();
    }
    return System.currentTimeMillis()-startTime;
}
public static long loopWithTryCatch(long loops)
{
    long startTime = System.currentTimeMillis();
    for (long i = 0L; i < loops; i++)
    {
        try {
            doSomething();
        } catch (Exception e) {
        }
    }
    return System.currentTimeMillis()-startTime;
}
public static void doSomething()
{
    for (int i = 0; i < 250; i++)
    {
        if (i % 3 == 0)
        {
            i++;
        }
    }
}
}

收到如下输出:

Loop without try catch: 375
Loop with try catch: 373

我很惊讶,所以我一遍又一遍地测试它,但我总是得到类似的结果,两种方式它运行在几乎相同的时间。

现在我的问题是:为什么?

我真的不理解它,据我所知,try-catch在使用某种表之前写入资源,然后-如果抛出任何异常-能够清理它并引用异常发生之前的值。

这至少需要一些时间,不是吗?我认为这可能是因为我随机选择的例子不能很好地代表它,在我测试的特定情况下,它不会减慢任何东西,但对我来说这似乎不太可能。

然后我想也许它只是花费了很少的时间,所以它没有注意到"很少"的执行量,所以我再次运行测试程序,总共有1000万个循环,但是我发现的只是证明了我已经发现的:两次执行花费的时间几乎相同。

那么,对于这种情况是否有任何合乎逻辑的解释,或者只是try-catch的特定示例行为?

感谢您提前澄清。

throw/catch块中的"慢"来自抛出和捕获异常的过程,而不是为它们设置"陷阱"的过程。当抛出异常时,JVM必须

  • 创建一个异常实例
  • 为堆栈跟踪准备空间
  • 将堆栈跟踪填充到准备的空间
  • "Unwind"堆栈到正确的位置
  • 将控制传递给异常处理程序。

当这些都不发生时,JVM只是在堆栈上标记一个异常处理程序在该级别可用,并继续执行实际的代码。

使特性无罚金是语言设计者的一个非常重要的目标:程序员不应该被要求为他们不使用的东西付费。否则,程序员可能会试图跳过异常处理,或者回到C语言中使用状态码的方式,以便在这里和那里节省一些CPU周期,将异常的结束拼写为一个功能。

从类似您的代码生成的字节码遵循以下模式

 1: invoke method
 2: goto 4
 3: store exception in catch block variable
   // would contain handling code if there was any
 4: return // or whatever comes after the try-catch
Exception table
   if an exception of the type in catch block happens from 1 to 3, goto 3

所以基本上你所添加的try-catch是一个额外的goto,如果没有异常发生。否则,JVM将在异常表中查找异常,并匹配异常发生的位置。然后,它将准备Exception并转到指定的任何指令索引。

最新更新