9

我想了解try {} catch {}块和堆栈跟踪是如何工作的。

我正在阅读这篇关于异常处理反模式的精彩文章,发现以下段落:

catch (NoSuchMethodException e) {
  throw new MyServiceException("Blah: " +
      e.getMessage());
}

这会破坏原始异常的堆栈跟踪,并且总是错误的。

在那之后,我意识到我真的不知道如何try/catch工作。我的理解如下。考虑这个例子:

void top() {
    try {
        f();
    } catch (MyException ex) {
        handleIt(); 
    } finally {
        cleanup();
    }
}

void f() {
    g();
}

void g() {
    throw new MyException();
}

当我调用top()时,调用链 在调用堆栈上top -> f -> g 留下两个堆栈帧(用于topf函数)。当在 中引发异常时g,程序会在执行堆栈中冒泡,直到找到try/catch处理异常的块。同时它释放堆栈帧并将堆栈跟踪信息附加到一些可以传递的“魔术”对象,catch并且可以打印堆栈跟踪。

它如何知道被调用的函数被 try/catch 块“包围”了?此信息是否绑定到堆栈帧?比如,一个指向错误处理块的指针(一些开关选择一个匹配的catch块),以及一个指向finally块的指针?为什么e.getMessage()在上面的例子中是破坏性的(见评论)?

注意,我知道如何使用 try/catch 和异常,我想知道它.

4

4 回答 4

10

“它怎么知道被调用的函数被 try/catch 块“包围”了?”

每个方法的代码都包含描述该方法的所有 try-catch 块的异常表。

当一个过程(函数、方法)被调用时,当前堆栈帧会附加调用指令的地址,以便在正确的指令处(调用指令的下一个)恢复该帧的执行。

当一个 throw 语句被执行时,JVM检查每个堆栈帧 ,以确定该帧是否可以处理异常。如果它的方法包含一个包含调用指令的try-catch块,并且块的异常类型是抛出异常的超类型(或相同),则可以。如果找到这样的帧,则该帧从 try-catch 块指向的指令恢复其执行。

于 2013-11-04T11:19:07.097 回答
4

当抛出异常时,完整的调用堆栈信息不是附加到某个魔术对象上,而是附加到创建的异常对象上。这不会在异常“冒泡”时发生 - 它在创建时发生并且它始终包含完整的调用链。

被调用函数不需要知道它被 try-catch-block 包围,它只是创建一个包含调用链的异常对象并将其传递给调用方法。这个方法必须决定它是否处理异常,因为它被一些捕获子句捕获,或者它是否进一步向上传递。直到到达调用链顶部并且VM处理它们的异常才会冒泡 - 通常通过打印堆栈跟踪并终止。

关于e.getMessage-example:完整的堆栈信息仅包含在原始异常中。在原始异常对象 e 被丢弃的给定示例中,仅将包含的消息传递给新创建的异常对象。并且那个异常只“知道”它自己的调用堆栈,所以附加到 e 的原始信息丢失了。

于 2013-11-04T10:17:17.167 回答
0

低层方法只是抛出异常,我们应该在高层处理它们。考虑你的例子。它应该像这样

void top() {
try {
    f();
} catch (MyException ex) {
    handleIt(); 
} finally {
    cleanup();
 }
}

void f() throws MyException {
try{
   g();
}catch(MyException e){
  throws new MyException("Error in g()",e); 
}

}

void g() throws MyException{
throw new MyException();
}
于 2013-11-04T10:19:17.637 回答
0

异常从最初抛出它的方法向上传播到调用堆栈,直到调用堆栈中的方法捕获它。如果方法 A 调用 B,而 B 调用 C,则调用堆栈如下所示:

A
B
C

当方法 C 返回时,调用堆栈只包含 A 和 B。异常从最初抛出它的方法向上传播到调用堆栈,直到调用堆栈中的方法捕获它。

当抛出异常时,该方法会在“throw”语句之后立即停止执行。“抛出”语句之后的任何语句都不会执行。当“catch”块在某处捕获到异常时,程序恢复执行。

如果一个方法调用另一个引发检查异常的方法,则调用方法将被强制传递异常或捕获它。捕获异常是使用 try-catch 块完成的。如果在 try-block 内调用的任何方法或执行的语句都没有抛出异常,则简单地忽略 catch-block。如果在 try-block 内抛出异常,例如从divide方法开始,调用方法callDivide的程序流程就像divide里面的程序流程一样被中断。程序流在调用堆栈中可以捕获抛出的异常的捕获块处恢复。

如果在 catch 块内抛出异常并且该异常没有被捕获,则 catch 块会像 try 块一样被中断。

http://tutorials.jenkov.com/java-exception-handling/basic-try-catch-finally.html https://www.geeksforgeeks.org/flow-control-in-try-catch-finally-in-java /

于 2020-12-16T05:58:04.103 回答