一个StackOverflowException如何检测? [英] How is a StackOverflowException detected?

查看:119
本文介绍了一个StackOverflowException如何检测?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我问我以为一个StackOverflowException问题是为了防止应用程序无限运行
机制。这是
不正确的。没有被检测到'国有企业' - 当
堆栈没有分配更多的内存容量抛出异常






这是一个普遍的问题,这可能有每个编程语言不同的答案。结果的我不知道比C#等语言如何处理堆栈溢出。



我是通过去例外今天一直在想如何一个StackOverflowException可以被检测到。我认为这是不可能说(比如)堆栈是1000调用深,则抛出该异常。结果因为也许在某些情况下,正确的逻辑将是深刻的。



什么是我的程序检测到一个无限循环的背后的逻辑?



SOE类:的 https://msdn.microsoft.com/de-de/library/system.stackoverflowexception%28v=vs.110%29.aspx 搜索结果
十字REF在SOE课堂上提到: HTTPS ://msdn.microsoft.com/de-de/library/system.reflection.emit.opcodes.localloc(v = vs.110)的.aspx 结果



注:的结果
我刚添加的堆栈溢出标签来这个问题,并描述说:当调用堆栈占用太多的内存就被抛出。这是否意味着调用堆栈是某种路径,我的程序的当前执行的位置,如果它不能存储更多的的路径信息的,则抛出异常?


< DIV CLASS =h2_lin>解决方案

堆栈溢出



我会很容易让你;但其实这是相当复杂的...请注意,我在这里归纳了不少。



正如你可能知道,大多数语言使用栈存放呼叫信息。另请参阅: https://msdn.microsoft.com/en-us/library/zkwh89ks.aspx为CDECL是如何工作的。如果你调用一个方法,你在栈上推的东西;如果你回来,你从栈中弹出的东西。



请注意,递归通常不是内联。 (注:我明确地说'递归'在这里,而不是尾递归;后者的作品就像一个'转到'并不会增加堆栈)。



检测堆栈溢出最简单的方法是检查你当前的堆栈深度(如字节使用) - 如果它击中的边界,给出一个错误。为了澄清这个边界检查:这些检查都做的方式通常是通过使用保护页;这意味着边界检查一般都不会实现为IF-THEN-ELSE检查(尽管一些实现存在...)。



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



检测无限循环



现在好了,这里有一个问题,我的天堂听到过一段时间。 : - )



基本上所有检测无限循环,需要你解决停机问题。这是方式的不可判定问题。这绝对不是由编译器完成



这并不意味着你不能做任何的分析;其实,你可以做相当多的分析。但是,请注意,有时你想要的东西(如Web服务器主循环)无限期地运行下去。



其他语言



同样有趣...功能语言使用递归,所以他们被堆基本约束。 (也就是说,函数式语言也倾向于使用尾递归,它的工作原理或多或少像一个'转到'和不成长堆栈。)



然后还有逻辑语言..现在好了,我不知道如何循环永远在 - 你可能会用的东西,根本不会计算(无解可以找到)结束。 (虽然,这可能取决于语言...)



屈服,异步延续



这是有趣的概念是,你可能会想到href=\"http://en.wikipedia.org/wiki/Continuation\">延续是称为收益第一次实施,真正的延续被视为实现。延续基本上允许你'保存'栈,继续在其他地方和恢复堆栈回在以后...(同样,细节比这要复杂得多,这仅仅是基本的想法)。



不幸的是微软并没有去这个想法(尽管我可以想像为什么),但通过使用一个辅助类来实现它。通过增加一个临时类和实习类内的所有局部变量产量和异步在C#中的工作。如果你打电话,做了收益率或异步的方法,你实际上创建的堆推的辅助类(从你打电话推堆栈上的方法内)。这是在堆上推类具有的功能(例如,用于收益这是枚举实现)。这样做的方法是使用一个状态变量,它存储在那里时,一个名为的MoveNext 程序应该继续的位置(例如,一些状态ID)。使用该ID的分支(开关)主罚剩下的事情。注意,该机制不执行任何特殊与堆栈本身的工作方式;你可以自己使用的类和方法实现相同的(它只是涉及到更多的打字: - ))



解决堆栈手动堆栈溢出



我总是喜欢一个很好的洪水填充。相机会在给你很多的递归地狱电话如果你这样做不对......比方说,像这样的:

 公共无效此时,floodFill(INT X,INT Y,INT颜色)
{
//等待死机的情况发生...
如果(有效(X,Y))
{
的setPixel(X,Y,颜色);
此时,floodFill(X - 1,Y,颜色);
此时,floodFill(X + 1,Y,颜色);
此时,floodFill(X,Y - 1,颜色);
此时,floodFill(X,Y + 1,颜色);
}
}

有什么不对的代码虽然。它所有的工作,但我们的堆栈的方式得到。具有手动堆栈解决了这一点,即使实现基本相同的:

 公共无效此时,floodFill(INT X,INT Y, INT颜色)
{
堆栈<元组LT; INT,INT>>堆栈=新的堆栈<元组LT; INT,INT>>();
stack.Push(新的记录< INT,INT>(X,Y));
而(stack.Count大于0)
{
无功电流= stack.Pop();

INT X2 = current.Item1;
INT Y2 = current.Item2;

//递归
如果(有效(X2,Y2))
{
的setPixel(X2,Y2,颜色);
stack.Push(新的记录< INT,INT>(X2-1,Y2));
stack.Push(新的记录< INT,INT>(2 + 1,Y2));
stack.Push(新的记录< INT,INT>(X2,Y2-1));
stack.Push(新的记录< INT,INT>(x2,y2 + 1));
}
}
}


When I asked the question I assumed a StackOverflowException is a mechanism in order to prevent applications to run infinitely. This is not true. A 'SOE' is not being detected - the exception is thrown when the stack does not have the capacity to allocate more memory.


This is a general question, which may has different answers per programming language.
I am not sure how languages other than C# handle a stack overflow.

I was going through Exceptions today and kept thinking about how a StackOverflowException can be detected. I think it is not possible to say (for example) if the stack is 1000 calls deep, then throw the exception.
Because maybe in some cases the correct logic will be that deep.

What is the logic behind the detection of an infinite loop in my program?

SOE Class: https://msdn.microsoft.com/de-de/library/system.stackoverflowexception%28v=vs.110%29.aspx

Cross ref mentioned in the SOE class: https://msdn.microsoft.com/de-de/library/system.reflection.emit.opcodes.localloc(v=vs.110).aspx

Note:
I just added the stack-overflow tag to this question, and the description says it is being thrown when the call stack consumes too much memory. Does that mean the call stack is some sort of path to the current executing position of my program and if it cannot store more path information, then the exception is thrown?

解决方案

Stack overflows

I'll make it easy for you; but this is actually quite complex... Note that I will generalize quite a bit here.

As you might know, most languages use the stack for storing call information. See also: https://msdn.microsoft.com/en-us/library/zkwh89ks.aspx for how cdecl works. If you call a method, you push stuff on the stack; if you return, you pop stuff from the stack.

Note that recursion isn't normally 'inlined'. (Note: I explicitly say 'recursion' here and not 'tail recursion'; the latter works like a 'goto' and doesn't grow the stack).

The easiest way to detect a stack overflow is to check your current stack depth (e.g. bytes used) - and if it hits a boundary, give an error. To clarify about this 'boundary check': the way these checks are done is normally by using guard pages; this means boundary checks aren't normally implemented as if-then-else checks (although some implementations exist...).

In most languages, each thread has its own stack.

Detecting infinite loops

Well now, here's a question I haven't heard for a while. :-)

Basically detecting all infinite loops requires you to solve the Halting Problem. Which is by the way an undecidable problem. This is definitely not done by compilers.

This doesn't mean you can't do any analysis; in fact, you can do quite a bit of analysis. However, also note that sometimes you want things to run indefinitely (such as the main loop in a web server).

Other languages

Also interesting... Functional languages use recursion, so they are basically bound by the stack. (That said, functional languages also tend to use tail recursion, which works more or less like a 'goto' and don't grow the stack.)

And then there's Logical languages.. well now, I'm not sure how to loop forever in that - you'll probably end up with something that won't evaluate at all (no solution can be found). (Though, this probably depends on the language... )

Yielding, async, continuations

An interesting concept is you might think of is called continuations. I've heard from Microsoft that when yield was first implemented, real continuations were considered as implementation. Continuations basically allow you to 'save' the stack, continue somewhere else and 'restore' the stack back at a later point... (Again, the details are much more complicated than this; this is just the basic idea).

Unfortunately Microsoft didn't go for this idea (although I can imagine why), but implemented it by using a helper class. Yield and async in C# work by adding a temporary class and interning all the local variables within the class. If you call a method that does a 'yield' or 'async', you actually create a helper class (from within the method you call and push on the stack) that's pushed on the heap. The class that's pushed on the heap has the functionality (e.g. for yield this is the enumeration implementation). The way this is done is by using a state variable, which stores the location (e.g. some state id) where the program should continue when MoveNext is called. A branch (switch) using this ID takes care of the rest. Note that this mechanism does nothing 'special' with the way the stack works itself; you can implement the same by yourself using classes and methods (it just involves more typing :-)).

Solving stack overflows with a manual stack

I always like a good flood fill. A picture will give you a hell of a lot of recursion calls if you do this wrong... say, like this:

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

There's nothing wrong with this code though. It does all the work, but our stack gets in the way. Having a manual stack solves this, even though the implementation is basically the same:

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

这篇关于一个StackOverflowException如何检测?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆