从 Scala 异常中获取参数

Get arguments from a scala exception

假设您在 Scala 中遇到异常(或 Java)。如何在堆栈跟踪中获取方法的参数?

参数包含在诊断故障时非常宝贵的信息。唉,我看不出有什么办法得到它们,尽管我相信它们应该还在记忆中。

catch(e) {
    x.getStackTrace.map(level => {
        // level has such methods as getClassName and getMethodName
        // but not the arguments.
        // Maybe the information is somewhere completely different?
        // In a Java equivalent of a core dump perhaps?
    })
}

你不能,至少在正常的程序执行中不能。

当抛出异常时,堆栈为 "unwound" 并在最近的适用 "catch" 块恢复执行。堆栈上的任何数据(包括方法参数)都会丢失到程序中并立即可用于 GC。

如果正在调试程序,那么您可以在抛出异常时设置断点并在调试器中检查方法参数。 (您可能可以使用 JVM 代理实现相同的效果。)

您当然可以将方法参数传递给异常构造函数。

参见:

后续问题:我们可以更改 Java 来做到这一点吗?

你说:

It looks as if there is an opportunity to make this better by changing the way exceptions are handled - by capturing the information before unwinding.

然后

How is the stack allocated in the JVM? What details of the method make it hard to get the arguments? If it is similar to stacks in say C then it is not obvious to me that it is necessary to unwind before executing the exception handler. Can not the handler run on the top of the stack or in a second stack with viewing rights to the first?

正如我在下面提到的,我认为这在不久的将来不太可能改变,因为堆栈在标准 JVM 上的分配方式。您建议的两个备选方案是很好的问题,我认为让我们了解为什么使用当前方法。

  1. 处理程序运行不能放在栈顶吗?

当然可以。这样做的主要缺点是每次处理异常时堆栈都会增长很多。

以下面的示例代码为例:

public Foo computeFoo() {
  try {
    // 1
    return firstVersion();
  } catch (Exception e) {
    // 2
    return secondVersion();
  }
}

假设我们通过一些方法调用 abc 到达了点“1”。堆栈帧可能如下所示:

[ a, b, c, computeFoo ]

假设 firstVersion 调用方法 x、y、z,并且在 "z" 中抛出异常。在抛出异常的那一刻,堆栈可能看起来像:

[ a, b, c, computeFoo, firstVersion, x, y, z ]

当我们移动到点 2 时,传统的 JVM 将能够立即丢弃来自 x、y、z 的所有数据,只需在移动到 secondVersion 之前通过 t运行cating 堆栈即可

[ a, b, c, computeFoo, secondVersion ]

根据您的建议,JVM 需要在堆栈上保留来自 x、y 和 z 的堆栈帧数据,以防“2”附近的任何代码想要访问参数:

[ a, b, c, computeFoo, firstVersion, x, y, z, secondVersion ]

应该清楚的是,一般来说,这可能会导致在使用异常的程序的堆栈中存储比目前需要更多的数据。在 JVM 中还需要一些额外的簿记来处理 "mixed" 正在使用和保留的堆栈帧堆栈,这将使维护复杂化并减慢速度。

鉴于 a) 异常通常不需要所有参数数据以及 b) 在需要时捕获特定参数数据有简单的解决方法,目前的标准权衡似乎更好。

  1. 第二个堆栈中的处理程序 运行 不能查看第一个堆栈的权限吗?

当然可以,但是对于 JVM 来说,这将需要更多的工作来实现,这会减慢一切的速度,但好处并不是特别引人注目。因此,当前的权衡。

始终值得牢记的是,这些系统并非 "handed down from the gods",而是由人们设计的,是多年进化、建立共识和权衡的结果。 JVM 如此工作的最重要原因之一是因为 C++ 曾经是这样工作的,这是大多数相关人员所期望和理解的。