【问题标题】:Understanding Java's stack unwinding on thrown exception了解 Java 在抛出异常时的堆栈展开
【发布时间】:2017-12-15 11:19:42
【问题描述】:

在关于使用异常与返回值的讨论和一些研究中,我想到了以下几点:

返回值更便宜且性能更好,而抛出异常需要创建新对象并展开堆栈。

现在,我阅读了堆栈展开,据我了解,这是在跳回堆栈上的下一帧之前释放当前堆栈帧上所有已分配资源的突然过程。特别是在 Java 中,不会释放任何资源,而是当前范围内的所有对象都被取消引用并符合 GC 条件。另一方面,在优雅返回的情况下,由于超出范围,对象会立即被销毁。

问题是这样的:

拥有一个声明了少量实例(例如 3 个)的方法,并假设如果抛出异常,它将被捕获到下一个级别(例如,它不会爬升 5 个堆栈级别),是否存在与返回值相比,性能方面的差异真的很明显(不确定这是否是正确的词)?我知道最终在一个简单的场景中不会有明显的差异,但它会导致一个更大的系统吗?

请假设在方法中的该点抛出异常是有意义的(这是一种异常情况)并且它不适用于控制流(这是一个已知错误)。仅出于性能原因,才会首选返回值。

【问题讨论】:

  • 你测试过这个吗?看起来它应该相当容易进行基准测试,至少是粗略的......(是的,我预计会有很大的不同。除了其他任何事情之外,抛出异常很可能需要捕获整个堆栈的异常详细信息,这比仅返回要昂贵得多。)
  • 再说你最后一段:如果是特殊情况,你为什么关心处理它的性能?
  • @JonSkeet 据我所知,只有在调用printStackTrace 或类似方法时才会捕获堆栈,因此可以避免。我也期待一个差异,但只有 1 个堆栈级别我认为它会被最小化(也假设我并不害怕将对象留给 GC)。 @T.J.Crowder 因为如果我非常关心性能,我可能会用完美的代码清晰度来换取不太理想的清晰度但更优化的性能。
  • @Konstantine:JIT 如何提前知道printStackTrace 是否会被调用? 一些信息需要在当时被捕获,即使它没有被呈现为字符串。在我看来,这仍然是您可以并且应该为自己测试的东西。 (而 T.J. Crowder 关于这是一个特殊情况的观点非常重要。)
  • 最好使用 jmh 之类的东西。

标签: java exception-handling stack-unwinding


【解决方案1】:

考虑到创建异常的 Stacktrace,java 不只是返回到直接调用者。 Java 一直上升到根堆栈。因此,根据您的小方法运行的深度,创建 Stacktrace 可能会很昂贵。

还要注意这个问题:Which part of throwing an Exception is expensive?

【讨论】:

  • 也许可以,但是您确定 Java 会抢先创建堆栈跟踪,还是仅在调用需要创建堆栈跟踪的方法时才执行此操作?我的信息说它是懒惰创建的,所以只要没有对.printStackTrace 的不必要调用,那么这个问题就无关紧要了
  • 如果您有资源,请将它们添加到问题中。这是一个基准:nurkiewicz.com/2012/10/where-do-stack-traces-come-from.html。无论如何,一旦堆栈展开,我看不出这会如何懒惰地发生。
猜你喜欢
  • 2014-02-16
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 2023-03-19
  • 1970-01-01
  • 2021-12-09
相关资源
最近更新 更多