C# 如何检测堆栈溢出?

jal*_*e13 0 c# stack-overflow recursion

我正在编写代码来解决问题,并且它适用于较小的输入,但是当增加到较大的输入时,我遇到了堆栈溢出异常:

Stack overflow.
Repeat 3239 times:
--------------------------------
   at Program+<>c__DisplayClass0_0.<<Main>$>g__Traverse|3(Point, Direction)
--------------------------------
   at Program.<<Main>$>g__Do|0_0()
Run Code Online (Sandbox Code Playgroud)

我还注意到,它声称重复的次数因执行而异,有时我也会得到 ~2300 次重复或 ~2800 次重复。

经过一段时间的调试后,我没有发现代码有任何问题,因此我手动将线程的堆栈大小设置为Int32.MaxValue,代码工作并产生了正确的答案。

我很好奇是否有人知道 C# 如何检测堆栈溢出?在这种情况下,在我看来,C# 过早地预测了堆栈溢出,即使程序确实有机会终止。

此外,为什么 C# 会给出堆栈溢出错误,指出调用已重复x多次,而x每次执行似乎都不同?

任何见解或信息表示赞赏!

Mar*_*ell 5

堆栈是一块连续的内存\xe2\x80\xa0(具有已定义的有限大小),由各种操作消耗(通常会立即再次释放),包括局部变量和瞬态变量的分配、“stackalloc”缓冲区等。调用通常会保留现有的所有内容,并在下一个空间中分配更多数据,因此深度递归操作很容易使用大量堆栈空间。

\n

这里没有发生“预测”;你只需到达内存块的末尾即可。这并不意味着会发生,并且意味着你的方法注定要失败——通常是:过于递归。保证堆栈每次都在完全相同的位置触底通常并不重要,因为您已经处于注定失败的场景中。这就像说发动机每次应该以 87432rpm 的转速爆炸。一些微小的变化可能会影响确切的可用空间或单个堆栈框架中使用的确切空间,但总的来说:这并不重要,您仍然使用了错误的方法。

\n

如果 C# 能够更好地进行尾部调用递归(这样就无需保留旧帧),那可能会很好,但是如果没有代码,我们甚至无法看到您的代码是否适合尾部调用。

\n

如果您有深度递归场景,通常可以通过将递归移出堆栈来解决堆栈的限制。通常,这意味着使用Queue<T>orStack<T>来保存逻辑状态,向其中添加种子值,然后使用一个简单的while循环(虽然不为空)来获取下一个值,进行一些思考,并可选择添加一些值或多个值以进行处理。

\n
\n

\xe2\x80\xa0 虚拟内存;其中的块可能或可能尚未映射到真实内存,但这并不重要

\n