C#捕获堆栈溢出异常

时间:2009-10-21 07:15:40

标签: c# try-catch stack-overflow

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

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

注意:如果相关:

  • 主线程中未抛出异常

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

9 个答案:

答案 0 :(得分:99)

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

  1. CLR正在托管环境 * 中运行,其中主机特别允许处理StackOverflow异常
  2. 用户代码抛出stackoverflow异常,而不是由于实际的堆栈溢出情况(Reference
  3. * “托管环境”,如“我的代码托管CLR并配置CLR的选项”而不是“我的代码在共享托管上运行”

答案 1 :(得分:44)

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

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

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();
    }
}

抓住奶酪吧。 ;)

答案 2 :(得分:36)

StackOverflowException s上的MSDN页面:

  

在.NET的早期版本中   框架,您的应用程序可以   捕获StackOverflowException对象   (例如,从中恢复   无限递归)。但是,那   目前不鼓励练习   因为重要的额外代码是   需要可靠地捕获堆栈   溢出异常并继续   程序执行。

     

从.NET Framework开始   版本2.0,StackOverflowException   try-catch无法捕获对象   块和相应的过程是   默认终止。所以,   建议用户编写代码   检测和防止堆栈   溢出。例如,如果你的   应用程序取决于递归,使用   反击或国家条件   终止递归循环。注意   托管的应用程序   公共语言运行库(CLR)可以   指定CLR卸载   应用程序域所在的堆栈   溢出异常发生并让   相应的过程继续。对于   更多信息,请参阅   ICLRPolicyManager接口和   托管公共语言运行时。

答案 3 :(得分:20)

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

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

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

答案 4 :(得分:15)

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

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

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

然而,你的应用程序将在退出事件函数后终止(一个非常脏的解决方法,是在这个事件中重新启动应用程序哈哈,没有这样做,也永远不会这样做)。但这对于伐木来说已经足够了!

  

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

答案 5 :(得分:6)

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

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

答案 6 :(得分:5)

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

答案 7 :(得分:5)

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

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

答案 8 :(得分:3)

这是不可能的,并且有充分的理由(对于一个人来说,考虑所有那些捕获(例外){}。

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