在.NET中遇到一些StackOverflowExceptions之后,我注意到它们完全绕过了.NET提供的未处理的异常处理程序(Application.ThreadException/AppDomain.UnhandledException).这非常令人不安,因为我们在这些异常处理程序中有关键的清理代码.
有没有办法克服这个问题?
有三种所谓的"异步异常".这是ThreadAbortException,OutOfMemoryException和提到的StackOverflowException.允许在代码中的任何指令处发生这些异常.
并且,还有一种方法可以克服它们:
最简单的是ThreadAbortException.当前代码在finally块中执行时.ThreadAbortExceptions有点"移动"到finally块的末尾.因此,最终块中的所有内容都不能被ThreadAbortException中止.
要避免OutOfMemoryException,您只有一种可能性:不要在堆上分配任何内容.这意味着您不能创建任何新的引用类型.
要克服StackOverflowException,您需要Framework的一些帮助.这种帮助体现在约束执行区域中.在执行实际代码之前分配所需的堆栈,并且还确保代码已经JIT编译,因此可以执行.
在Constrained Execution Regions中执行代码有三种形式(从BCL团队博客复制):
ExecuteCodeWithGuaranteedCleanup,一个try/finally的堆栈溢出安全形式.
try/finally块之后立即调用RuntimeHelpers.PrepareConstrainedRegions.try块不受约束,但是所有catch,最后和try块的故障块都是.
作为一个关键的终结器 - CriticalFinalizerObject的任何子类都有一个终结器,它在分配对象的实例之前急切地准备好.
一个特例是SafeHandle的ReleaseHandle方法,一种在分配子类之前急切准备的虚方法,并从SafeHandle的关键终结器调用.
您可以在这些博文中找到更多信息:
受约束的执行区域和其他勘误[Brian Grunkemeyer]在BCL团队博客.
Joe Duffy关于Atomicity和异步异常失败的Weblog ,他非常了解.net Framework中的异步异常和健壮性.
并不是的; 在CLR本身内发生堆栈溢出或内存不足异常意味着出现了严重错误(当我成为笨蛋并创建递归属性时,我通常会得到它).
当这种状态发生时,CLR无法分配新的函数调用或内存以使其能够调用异常处理程序; 这是一个"我们必须停止现在 "的情景.
但是,如果您自己抛出异常,则会调用异常处理程序.