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

flo*_*oon 7 java try-catch

今天我意识到一些对我来说很奇怪的事情:当我这样做时,我注意到了

try {
    doSomething();
} catch (Exception e) {
}
Run Code Online (Sandbox Code Playgroud)

它并不比我刚刚做的慢

doSomething();
Run Code Online (Sandbox Code Playgroud)

所以我运行了一个测试并写下了一些快速代码来证明我所看到的,代码基本上只是循环遍历一个名为doS​​omething()的函数很多次,一次没有,一次是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++;
        }
    }
}
}
Run Code Online (Sandbox Code Playgroud)

我收到了以下输出:

Loop without try catch: 375
Loop with try catch: 373
Run Code Online (Sandbox Code Playgroud)

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

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

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

这应该至少花一些时间,不应该吗?我认为这可能是因为我选择的随机例子并没有恰当地代表它,而在我测试它的特定情况下它并没有减慢任何速度,但这对我来说似乎不太可能.

然后我想也许只需要这么短的时间就不会有那么少的执行量,所以我再次运行了测试程序,总共有1000万次循环,但我发现只是证明了我的意思已经发现:两次执行都需要几乎相同的时间.

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

感谢您提前澄清.

das*_*ght 9

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

  • 创建例外实例
  • 为堆栈跟踪准备空间
  • 将堆栈跟踪填充到准备好的空间中
  • 将堆栈"放松"到正确的位置
  • 将控件传递给异常处理程序.

当没有发生这种情况时,JVM只是简单地说明堆栈上此级别的异常处理程序可用,并继续执行实际代码.

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


Sot*_*lis 6

从像你这样的代码生成的字节码遵循这种模式

 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
Run Code Online (Sandbox Code Playgroud)

因此,try-catch如果没有异常发生,基本上你添加的所有内容都是额外的goto.否则,JVM将在异常表中查找异常并匹配它发生的位置.然后它将准备Exception并转到指定的任何指令索引.整个行动很重.

  • @BoratSagdiyev如果您有已编译的`.class`文件,您可以使用`javap`应用程序检查人类可读形式的字节码. (5认同)