132

我对引发堆栈溢出异常的方法进行了递归调用。第一次调用被 try catch 块包围,但没有捕获到异常。

堆栈溢出异常是否以特殊方式表现?我可以正确捕获/处理异常吗?

不确定是否相关,但附加信息:

  • 主线程中没有抛出异常

  • 代码引发异常的对象由 Assembly.LoadFrom(...).CreateInstance(...) 手动加载

4

9 回答 9

118

从 2.0 开始,只能在以下情况下捕获 StackOverflow 异常。

  1. CLR 在托管环境中运行*主机专门允许处理 StackOverflow 异常
  2. stackoverflow 异常是由用户代码引发的,而不是由于实际的堆栈溢出情况(参考

* “托管环境”,如“我的代码托管 CLR,我配置 CLR 的选项”,而不是“我的代码在共享托管上运行”

于 2009-10-21T07:20:08.173 回答
51

正确的方法是修复溢出,但是....

你可以给自己一个更大的筹码:-

using System.Threading;
Thread T = new Thread(threadDelegate, stackSizeInBytes);
T.Start();

您可以使用 System.Diagnostics.StackTrace FrameCount 属性来计算您使用的帧数,并在达到帧数限制时抛出您自己的异常。

或者,您可以计算剩余堆栈的大小并在低于阈值时抛出您自己的异常:-

class Program
{
    static int n;
    static int topOfStack;
    const int stackSize = 1000000; // Default?

    // The func is 76 bytes, but we need space to unwind the exception.
    const int spaceRequired = 18*1024; 

    unsafe static void Main(string[] args)
    {
        int var;
        topOfStack = (int)&var;

        n=0;
        recurse();
    }

    unsafe static void recurse()
    {
        int remaining;
        remaining = stackSize - (topOfStack - (int)&remaining);
        if (remaining < spaceRequired)
            throw new Exception("Cheese");
        n++;
        recurse();
    }
}

只是抓住奶酪。;)

于 2009-10-21T11:33:21.820 回答
44

StackOverflowException上的 MSDN 页面:

在 .NET Framework 的早期版本中,您的应用程序可以捕获 StackOverflowException 对象(例如,从无界递归中恢复)。但是,目前不鼓励这种做法,因为需要大量额外的代码才能可靠地捕获堆栈溢出异常并继续执行程序。

从 .NET Framework 2.0 版开始,StackOverflowException 对象无法被 try-catch 块捕获,并且默认情况下会终止相应的进程。因此,建议用户编写代码来检测和防止堆栈溢出。例如,如果您的应用程序依赖于递归,请使用计数器或状态条件来终止递归循环。请注意,承载公共语言运行时(CLR)的应用程序可以指定CLR卸载发生堆栈溢出异常的应用程序域,并让相应的进程继续进行。有关详细信息,请参阅 ICLRPolicyManager 接口和托管公共语言运行时。

于 2009-10-21T07:19:08.287 回答
29

正如几位用户已经说过的那样,您无法捕获异常。但是,如果您正在努力找出它发生的位置,您可能需要将 Visual Studio 配置为在它被抛出时中断。

为此,您需要从“调试”菜单中打开异常设置。在旧版本的 Visual Studio 中,这是在 'Debug' - 'Exceptions';在较新的版本中,它位于“调试”-“Windows”-“异常设置”。

打开设置后,展开“Common Language Runtime Exceptions”,展开“System”,向下滚动并检查“System.StackOverflowException”。然后您可以查看调用堆栈并查找调用的重复模式。这应该让您了解在哪里寻找修复导致堆栈溢出的代码。

于 2009-10-21T08:29:18.347 回答
16

如上所述,由于进程状态损坏,无法捕获系统引发的 StackOverflowException。但是有一种方法可以将异常视为事件:

http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

从 .NET Framework 版本 4 开始,不会针对破坏进程状态的异常(例如堆栈溢出或访问冲突)引发此事件,除非事件处理程序是安全关键的并且具有 HandleProcessCorruptedStateExceptionsAttribute 属性。

尽管如此,您的应用程序将在退出事件函数后终止(一个非常肮脏的解决方法,是在此事件中重新启动应用程序哈哈,没有这样做,也永远不会这样做)。但它足以记录日志!

在 .NET Framework 版本 1.0 和 1.1 中,发生在主应用程序线程以外的线程中的未处理异常会被运行时捕获,因此不会导致应用程序终止。因此,可能会在应用程序终止的情况下引发 UnhandledException 事件。从 .NET Framework 2.0 版开始,删除了对子线程中未处理异常的支持,因为此类静默故障的累积影响包括性能下降、数据损坏和锁定,所有这些都难以调试。有关更多信息,包括运行时不终止的情况列表,请参阅托管线程中的异常。

于 2011-07-28T09:33:27.457 回答
6

是的,从 CLR 2.0 开始,堆栈溢出被认为是不可恢复的情况。所以运行时仍然关闭进程。

有关详细信息,请参阅文档http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx

于 2009-10-21T07:18:50.127 回答
5

你不能。CLR 不会让你。堆栈溢出是致命错误,无法从中恢复。

于 2009-10-21T07:19:35.383 回答
5

您不能像大多数帖子所解释的那样,让我添加另一个区域:

在许多网站上,您会发现人们说避免这种情况的方法是使用不同的 AppDomain,因此如果发生这种情况,该域将被卸载。这是绝对错误的(除非您托管 CLR),因为 CLR 的默认行为会引发 KillProcess 事件,从而关闭您的默认 AppDomain。

于 2010-02-11T08:39:26.590 回答
3

这是不可能的,并且有充分的理由(首先,想想周围的所有那些 catch(Exception){})。

如果要在堆栈溢出后继续执行,请在不同的 AppDomain 中运行危险代码。可以设置 CLR 策略以在溢出时终止当前 AppDomain 而不会影响原始域。

于 2009-10-21T07:33:03.717 回答