为什么抛出异常这么慢?

时间:2013-06-29 11:12:17

标签: .net performance exception internals

他们告诉我们不要使用异常来控制我们程序的流程,因为抛出异常很慢。我从来没有听到任何解释为什么抛出异常这么慢。

所以问题是:

  

抛出异常的机制是什么?涉及哪些特定操作可能会对性能产生影响?

编辑:

一些澄清:我想听听操作系统处理抛出异常所需的额外工作。在用户和内核模式之间是否有一些切换成本很高?或者构建异常对象可能代价高昂?或者也许有一些东西切换程序流程我错过了什么?我的问题是编程语言不可知(我希望如此,但证明我错了)。但是,如果您需要一些锚点,那么我对与此主题相关的.NET内部结构感兴趣。

EDIT2:

我对异常性能没有任何问题。我只是想了解这种机制的内部结构。

EDIT3:

让我的问题更清楚。

4 个答案:

答案 0 :(得分:6)

异常处理需要一些复杂性和“魔力”。借调@Joni的回应,主要成本是收集堆栈跟踪。抛出异常时,运行时必须向下遍历堆栈的激活记录,寻找兼容的异常处理程序,最后执行finally阻止每一步。所有这些都必须在运行时发生;它无法由编译器修复。在像C ++这样的语言中,必须执行析构函数。

异常处理本质上是一种带外“特殊”处理模式。加速正常执行的事情(如缓存)也不起作用。 (我想这里的参考地点要差很多)。这个处理可以被优化,但由于exc处理被认为是“特殊的”,所以它的关注度较低。

答案 1 :(得分:3)

在应用程序级别创建例外;它们没有操作系统支持。没有特别的理由为什么抛出和捕获异常应该比任何其他非本地控制转移慢,比如调用或从函数返回。

使异常“慢”的原因是返回错误代码的替代方案取决于编程环境细节所需的额外工作。例如,在Java中,抛出异常的最慢部分是填充堆栈跟踪。

答案 2 :(得分:2)

使用异常进行流量控制是一个坏主意的主要原因是我应该能够附加一个调试器并告诉它在异常中断...并且它在例外情况下中断。如果代码使用异常进行流量控制并且在正常操作下它会不断中断,那么在调整异常等情况下进行调试变得非常困难......但是最后可能会错过一个真正的例外情况。

如果我编写验证正功能的测试,我应该能够使用附加的调试器运行它们并且它不会捕获。如果你不能这样做,你可能会滥用例外。

一个例子:假设我有代码来获取id的项目。我的代码最终发现该id的项目不存在。我应该抛出NotFoundException吗?这是一个争论点 - 我会说不 - 这里没有发生异常/错误。您的代码正确地找不到该ID存在的任何内容,并且没有发现错误的错误。在这些情况下,我发现异常被滥用并导致异常被抛入非抢劫案件中。

答案 3 :(得分:2)

您被误导了,但实际代码的结果可能不会改变。 .NET中的异常处理系统实际上非常快,这意味着它的性能可与其他错误处理选项相媲美。但是,如在另一个答案中提到的那样,您只想适当地使用异常 - 即仅在您正常运行应用程序期间不希望发生的异常情况。原因如下:

  • 分析涉及异常的代码流比不涉及异常的代码更复杂,因此您的“普通”代码应避免抛出异常。
  • 每次抛出异常时,Visual Studio调试器都会报告。它只是输出窗口中的一行,但如果你滥用异常,那么这个奇妙的功能很快就会变成一场噩梦。
  • Visual Studio能够在抛出特定类型的异常时中断。如果您始终正确使用异常(为了其预期目的,并且仅用于指示该类型的实际异常),此功能提供了一种快速调试应用程序中与错误处理(恢复,报告等)相关的错误的方法。 / LI>

在处理实际异常情况时,异常处理实际上提供了比返回错误代码等其他方面的性能优势。当涉及分支预测和提示时,JIT可以假设代码将从不抛出异常,允许它生成有效使用处理器的任何可用分支预测功能的代码,以避免分支开销对于包含错误处理功能但未主动处理错误的代码。