假设您在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?
})
}
答案 0 :(得分:1)
你不能,至少不能正常执行程序。
当抛出异常时,堆栈将被解除"并在最近适用的" catch"恢复执行。块。堆栈中的任何数据(包括方法参数)都会丢失到程序中并立即可用于GC。
如果正在调试程序,那么可以在抛出异常时设置断点并检查调试器中的方法参数。 (您可以使用JVM代理实现相同的效果。)
您当然可以将方法参数传递给异常构造函数。
请参阅:
你说:
通过改变处理异常的方式 - 通过在展开前捕获信息,看起来好像有机会做到这一点。
然后
如何在JVM中分配堆栈?该方法的哪些细节使得很难获得参数?如果它类似于C中的堆栈那么对我来说,在执行异常处理程序之前必须放松是不明显的。处理程序不能在堆栈顶部运行,也不能在第二个堆栈中运行,而第一个堆栈具有查看权限?
正如我在下面提到的,我认为由于堆栈在标准JVM上的分配方式,这在不久的将来不太可能发生变化。你的两个建议的替代方案是好问题,我想让我们看看为什么使用当前的方法。
当然可以。这样做的主要缺点是每次处理异常时堆栈会增长很多。
采用以下示例代码:
public Foo computeFoo() {
try {
// 1
return firstVersion();
} catch (Exception e) {
// 2
return secondVersion();
}
}
说我们到达了点#" 1"通过某种方法调用a
,b
,c
。堆栈帧堆栈可能看起来像:
[ a, b, c, computeFoo ]
让我们说firstVersion
调用方法x,y,z,并在#34; z"内部抛出异常。抛出异常时,堆栈可能如下所示:
[ a, b, c, computeFoo, firstVersion, x, y, z ]
当我们转到第2点时,传统的JVM可以立即丢弃x,y,z中的所有数据,只需截断堆栈,然后再进入secondVersion
[ a, b, c, computeFoo, secondVersion ]
根据您的提议,JVM需要保留堆栈中x,y和z的堆栈帧数据,以防任何代码接近" 2"想要访问参数:
[ a, b, c, computeFoo, firstVersion, x, y, z, secondVersion ]
应该很清楚,一般来说,这可能会导致在使用异常的程序中将更多数据存储在堆栈中,而不是目前需要的。在JVM中还需要一些额外的簿记来处理"混合"堆栈的使用中和保留的堆栈帧,这将使维护变得复杂并减慢速度。
鉴于a)异常通常不需要所有参数数据,b)在需要时可以轻松捕获特定参数数据,这里的当前标准权衡似乎更好。
当然,这可能会让JVM实施更多的工作,这会减慢一切,以获得不太引人注目的好处。因此目前的权衡。
值得注意的是,这些系统不是从神灵传下来的,而是由人们设计的,是多年演变,建立共识和权衡的结果。 JVM工作的最重要原因之一是因为C ++曾经像这样工作,这是大多数人所期望和理解的。