13

在 .NET 中遇到一些 StackOverflowExceptions 后,我注意到它们完全绕过了 .NET 提供的未处理异常处理程序(Application.ThreadException / AppDomain.UnhandledException)。这非常令人不安,因为我们在这些异常处理程序中有关键的清理代码。

有什么办法可以克服这个吗?

4

4 回答 4

25

存在三种所谓的“异步异常”。那是 ThreadAbortException、OutOfMemoryException 和提到的 StackOverflowException。这些异常允许发生在代码中的任何指令中。

而且,还有一种方法可以克服它们:

最简单的是 ThreadAbortException。当前代码在 finally 块中执行时。ThreadAbortExceptions 有点“移动”到 finally 块的末尾。因此,finally 块中的所有内容都不能被 ThreadAbortException 中止。

要避免 OutOfMemoryException,您只有一种可能性:不要在堆上分配任何东西。这意味着您不能创建任何新的引用类型。

要克服 StackOverflowException,您需要框架的一些帮助。这种帮助体现在受约束的执行区域中。在执行实际代码之前分配所需的堆栈,另外还确保代码已经 JIT 编译,因此可以执行。

在受约束的执行区域中执行代码有三种形式(复制自BCL 团队博客):

  • ExecuteCodeWithGuaranteedCleanup,try/finally 的堆栈溢出安全形式。
  • 紧接在调用 RuntimeHelpers.PrepareConstrainedRegions 之前的 try/finally 块。try 块不受约束,但该 try 的所有 catch、finally 和 fault 块都是。
  • 作为关键终结器 - CriticalFinalizerObject 的任何子类都有一个终结器,该终结器在分配对象实例之前就已准备好。
    • 一个特例是 SafeHandle 的 ReleaseHandle 方法,它是一个在分配子类之前热切准备的虚拟方法,并从 SafeHandle 的关键终结器调用。

您可以在这些博客文章中找到更多信息:

BCL 团队博客中的受限执行区域和其他勘误表 [Brian Grunkemeyer]

Joe Duffy 的关于原子性和异步异常失败的博客,其中他很好地概述了 .net 框架中的异步异常和健壮性。

于 2008-09-20T12:33:05.673 回答
18

并不真地; 堆栈溢出或 CLR 本身发生内存不足异常意味着出现严重错误(当我一直是个笨蛋并创建递归属性时,我通常会得到它)。

当此状态发生时,CLR 无法分配新的函数调用或内存以使其能够调用异常处理程序;这是一个“我们现在必须停止”的场景。

但是,如果您自己抛出异常,您的异常处理程序将被调用。

于 2008-09-20T09:37:58.590 回答
1

堆栈溢出不是您可以从中恢复的东西,因为它甚至无法分配更多堆栈内存来调用您的异常处理程序。

你唯一能做的就是找出原因并防止它发生(例如,小心递归,不要在堆栈上分配大对象)。

于 2008-09-20T09:41:02.407 回答
1

吹镖把它钉在上面。Dumbass 递归属性,他喜欢这样称呼它。真的只是输入代码太快的问题。

private Thing _myThing = null;

Public Thing MyThing
{
   get{
        return this.MyThing;}
   set{
        this.MyThing = value;}
}
于 2010-06-01T19:07:41.150 回答