`try/catch` 是如何工作的

2022-01-22 00:00:00 exception-handling exception stack java

我想了解 try {} catch {} 块和堆栈跟踪的工作原理.

我正在阅读 这篇关于异常处理的好文章模式 并找到以下段落:

<块引用>

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

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

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

void top() {尝试 {F();} 捕捉(MyException ex){处理它();} 最后 {清理(​​);}}无效 f() {G();}无效 g() {抛出新的 MyException();}

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

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

注意,我知道如何使用 try/catch 和异常,我想知道它在内部是如何工作的.

解决方案

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

每个方法的代码包含异常表,它描述了该方法的所有 try-catch 块.

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

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

I would like to the innards of how does try {} catch {} block and stack traces work.

I was reading this great article about exception handling anti-patterns and found the following paragraph:

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

This destroys the stack trace of the original exception, and is always wrong.

After that I realized that I don't really know how try/catch works. My understanding is the following. Consider the example:

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

void f() {
    g();
}

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

When I call top(), the call chain top -> f -> g leaves two stack frames on the call stack (for top and f functions). When the exception is raised in g, the program bubbles up the execution stack until it finds try/catch block that handles the exception. Meanwhile it frees the stack frames and attach stack trace information to some "magic" object that can be passed to catch and the stack trace can be printed.

How does it know that the called function is "surrounded" with the try/catch block? Is this information bound to the stack frame? Like, a pointer to error handling block (some switch selecting a matching catch block), and a pointer to finally block? Why e.getMessage() is destructive in the example above (see the comments)?

Note, I know how to use try/catch and exceptions, I want to know how does it work inside.

解决方案

"How does it know that the called function is "surrounded" with the try/catch block?"

The code of each method contains Exception Table which describes all try-catch blocks of that method.

When a procedure (function, method) is called, the current stack frame is appended with the address of the calling instruction, so as to restore execution of that frame at the correct instruction (next after calling instruction).

When a throw statement is executed, the JVM examines each stack frame to find out if that frame can handle the exception. It can if its method contains a try-catch block which contains the calling instruction, and the type of block's exception is a supertype (or the same as) of the thrown exception. If such a frame is found, the frame restores its execution from the instruction pointed to from the try-catch block.

相关文章