C # 捕获堆栈溢出异常

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

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

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

  • 不会在主线程中引发异常

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

124417 次浏览

是的,从 CLR 2.0堆栈溢出被认为是不可恢复的情况。

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

来自 StackOverflow 异常上的 MSDN 页面:

在.NET 的早期版本中 框架,您的应用程序可以 捕获 StackOverfloException 对象 (例如: 无限递归) 目前不鼓励练习 因为重要的附加代码是 要求可靠地捕获一堆 溢出异常并继续 程序执行。

从.NETFramework 开始 版本2.0,一个 StackOverflow 异常 对象不能被 try-catch 捕获 块,并且相应的进程是 默认终止。因此, 建议用户编写代码 检测和防止堆栈 溢出。例如,如果您的 应用程序依赖于递归,使用 计数器或状态条件 终止递归循环。注意 一个承载 通用语言运行库 指定 CLR 卸载 其中堆栈的应用程序域 发生溢出异常,并让 相应的程序继续 更多信息,请参见 接口和 举办通用语言运行库。

不行。 CLR 不允许。堆栈溢出是一个致命错误,无法从中恢复。

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

  1. CLR 在宿主环境 *中运行,宿主特别允许处理 StackOverflow 异常
  2. 堆栈溢出异常是由用户代码引发的,而不是由于实际的堆栈溢出情况(参考文献)引发的

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

这是不可能的,而且有充分的理由(举个例子,想想周围所有那些捕获(例外){})。

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

正如一些用户已经说过的,您无法捕捉到异常。然而,如果您正在努力找出它发生在哪里,您可能想要配置视觉工作室中断时抛出。

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

打开设置后,展开“通用语言运行库异常”,展开“系统”,向下滚动并检查“系统”。StackOverflow 异常’。然后您可以查看调用堆栈并查找调用的重复模式。这应该可以让您了解在哪里可以修复导致堆栈溢出的代码。

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

你可以给自己一个更大的堆栈:-

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

您可以使用 System。诊断。属性来计算所使用的帧的数量,并在达到帧限制时引发自己的异常。

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

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

抓住奶酪

正如大多数文章所解释的那样,你无法做到这一点,让我补充另一个方面:

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

正如前面多次提到的,由于进程状态损坏,不可能捕获 System 引发的 StackOverflow 异常。但是有一种方法可以将异常作为一个事件来注意:

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版本中,这个针对子线程中未处理异常的备份被删除,因为这种无声故障的累积效应包括性能下降、数据损坏和锁定,所有这些都很难调试。有关更多信息(包括运行库不终止的情况列表) ,请参见托管线程中的异常。