【问题标题】:StackOverflowException in .NET.NET 中的 StackOverflowException
【发布时间】:2010-09-11 14:04:16
【问题描述】:

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

有什么办法可以克服吗?

【问题讨论】:

    标签: .net exception


    【解决方案1】:

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

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

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

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

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

    在Constrained Execution Regions中执行代码有3种形式(抄自BCL Team Blog):

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

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

    Constrained Execution Regions and other errata [Brian Grunkemeyer] 在 BCL 团队博客上。

    Joe Duffy 的关于 Atomicity and asynchronous exception failures 的博客,其中他很好地概述了 .net 框架中的异步异常和稳健性。

    【讨论】:

    • 请注意,如果预先分配,并不是所有必要的堆栈空间,只有大约 12 页(48K 或 96K)。如果关键代码需要更多空间,它将失败,CER 将无济于事。 CER 只提高了一点执行保证,并不提供 100% 的保证。
    【解决方案2】:

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

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

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

    【讨论】:

    • 我觉得有点奇怪,毕竟 clr 是微软拥有的代码,他们本可以节省一些堆栈帧来允许异常处理。
    • 它怎么知道要预留多少?在堆栈溢出的情况下,堆栈本身已经增长到无法处理的大小。通过保留一些空间,您需要保留最大堆栈大小,这反过来又是软件可用堆栈的一半。
    • 好吧,我明白了……有点……在单线程环境中。但是,嘿,我们可以创建多个线程,不是吗?每个线程都有一个单独的堆栈,不是吗?那为什么只有一个线程出栈就终止整个进程呢?
    • “当我是个笨蛋并创建了一个递归属性时,我通常会得到它” - 谢谢。它从字面上向我指出了问题所在。拥有一组计算属性,有些依赖于其他属性....一个小错误和繁荣 - OOM 异常。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      private Thing _myThing = null;
      
      Public Thing MyThing
      {
         get{
              return this.MyThing;}
         set{
              this.MyThing = value;}
      }
      

      【讨论】:

        猜你喜欢
        • 2011-04-03
        • 1970-01-01
        • 2011-11-19
        • 1970-01-01
        • 2016-12-24
        • 2011-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多