C# 如何检测StackOverflowException?

C# 如何检测StackOverflowException?,c#,stack-overflow,infinite-loop,C#,Stack Overflow,Infinite Loop,TL;TR 当我问这个问题时,我假设一个StackOverflowException是一个 防止应用程序无限运行的机制。这不是真的。 未检测到StackOverflowException。 它是在 堆栈没有能力分配更多内存。 [原问题:] 这是一个一般性问题,每种编程语言可能有不同的答案。 我不确定C语言以外的其他语言如何处理堆栈溢出 今天我遇到了异常,一直在思考如何检测StackOverflowException。我认为如果堆栈有1000个调用,那么抛出异常是不可能的。因为在某些情况下,正确的

TL;TR
当我问这个问题时,我假设一个
StackOverflowException
是一个 防止应用程序无限运行的机制。这不是真的。
未检测到
StackOverflowException

它是在 堆栈没有能力分配更多内存。

[原问题:]

这是一个一般性问题,每种编程语言可能有不同的答案。
我不确定C语言以外的其他语言如何处理堆栈溢出

今天我遇到了异常,一直在思考如何检测
StackOverflowException
。我认为如果堆栈有1000个调用,那么抛出异常是不可能的。因为在某些情况下,正确的逻辑可能会如此深刻

在我的程序中检测无限循环的逻辑是什么?

StackOverflowException
class:


StackOverflowException
类文档中提到的交叉引用:


我刚刚在这个问题中添加了
stack overflow
标记,描述中说当调用堆栈占用太多内存时会抛出该标记。这是否意味着调用堆栈是指向程序当前执行位置的某种路径,如果它不能存储更多的路径信息,则会引发异常?

堆栈溢出的问题不是它们可能来自无限计算。问题是堆栈内存耗尽,在当今的操作系统和语言中,堆栈内存是一种有限的资源


当程序试图访问超出分配给堆栈的内存部分时,会检测到这种情况。这会导致异常。

堆栈只是一个固定大小的内存块,在创建线程时分配。还有一个“堆栈指针”,这是一种跟踪当前使用了多少堆栈的方法。作为创建新堆栈框架的一部分(当调用方法、属性、构造函数等时),它将堆栈指针向上移动新框架所需的量。此时,它将检查是否已将堆栈指针移过堆栈的末尾,如果是,则抛出一个SOE

该程序不检测无限递归。无限递归(当运行时被迫为每次调用创建一个新的堆栈框架时),它只会导致执行如此多的方法调用,从而填满这个有限的空间。您可以同样轻松地使用有限数量的嵌套方法调用填充有限的空间,这些方法调用恰好消耗了比堆栈更多的空间。(这往往很难做到;这通常是由递归的方法引起的,不是无限的,而是具有足够的深度,堆栈无法处理它。)

警告:这与引擎盖下的机制有很大关系,包括CLR本身必须如何工作。只有当您开始学习汇编级编程时,这才真正有意义

在后台,方法调用是通过将控制权传递给另一个方法的站点来执行的。为了传递参数和返回,这些参数被加载到堆栈中。为了知道如何将控制权返回给调用方法,CLR还必须实现一个调用堆栈,该堆栈在调用方法时推送到,在方法返回时弹出。此堆栈告诉返回方法将控件返回到何处

由于计算机只有有限的内存,有时调用堆栈会变得太大。因此,
StackOverflowException
不是无限运行或无限递归程序的检测,而是计算机无法再处理跟踪方法需要返回的位置、必要的参数、返回、变量或(更常见)所需的堆栈大小的检测它们的组合。在无限递归期间发生这种异常是因为逻辑不可避免地会覆盖堆栈

为了回答您的问题,如果一个程序故意具有会使堆栈过载的逻辑,那么您将看到一个
StackOverflowException
。然而,这通常是数千到数百万个调用的深度,除非您创建了一个无限递归循环,否则很少有实际问题


附录:我之所以提到递归循环,是因为只有在对堆栈进行过度处理的情况下才会发生异常——这通常意味着调用的方法最终会回调到同一个方法中,并增加调用堆栈。如果您有逻辑上无限但不是递归的东西,您通常不会看到
堆栈溢出异常

堆栈溢出

我会让你轻松一点;但这实际上相当复杂。。。注意,我将在这里进行相当多的概括

您可能知道,大多数语言都使用堆栈来存储调用信息。另请参见:了解cdecl的工作原理。如果你调用一个方法,你就把东西推到堆栈上;如果你回来,你会从堆栈中弹出东西

请注意,递归通常不是“内联”的。(注意:我在这里明确地说‘递归’,而不是‘尾部递归’;后者的工作方式类似于‘goto’,不会增加堆栈)

检测堆栈溢出的最简单方法是检查当前堆栈深度(例如使用的字节数),如果它到达边界,则给出一个错误。澄清“边界检查”:这些检查通常是通过使用保护页进行的;这意味着边界检查通常不会像else检查那样实现(尽管存在一些实现…)

在大多数语言中,每个线程都有自己的堆栈

检测无限循环

现在,我有一个问题好久没听到了。:-)

基本上,检测所有无限循环需要您解决以下问题。顺便说一句,这是一个。这绝对是
public void FloodFill(int x, int y, int color)
{
    // Wait for the crash to happen...
    if (Valid(x,y))
    {
        SetPixel(x, y, color);
        FloodFill(x - 1, y, color);
        FloodFill(x + 1, y, color);
        FloodFill(x, y - 1, color);
        FloodFill(x, y + 1, color);
    }
}
public void FloodFill(int x, int y, int color)
{
    Stack<Tuple<int, int>> stack = new Stack<Tuple<int, int>>();
    stack.Push(new Tuple<int, int>(x, y));
    while (stack.Count > 0)
    {
        var current = stack.Pop();

        int x2 = current.Item1;
        int y2 = current.Item2;

        // "Recurse"
        if (Valid(x2, y2))
        {
            SetPixel(x2, y2, color);
            stack.Push(new Tuple<int, int>(x2-1, y2));
            stack.Push(new Tuple<int, int>(x2+1, y2));
            stack.Push(new Tuple<int, int>(x2, y2-1));
            stack.Push(new Tuple<int, int>(x2, y2+1));
        }
    }
}