堆栈溢出在.Net垃圾收集世界中实际意味着什么?
答案 0 :(得分:11)
与其他任何地方完全相同的事情 - 你已经炸掉了堆栈,通常是因为你的回复很糟糕。例如:
public int Foo(int x)
{
return Foo(x + 1);
}
现在这个示例可以通过尾递归进行优化,在这种情况下它将永远运行 - 但是否则(在更一般的情况下,尾递归不可行),这将为每个递归调用推送一个新的堆栈帧Foo
...并且永远不会弹出那些堆栈帧,因为调用永远不会实际返回。
这与垃圾收集无关。
答案 1 :(得分:3)
堆栈溢出和垃圾收集本质上是正交的概念:堆栈与堆。垃圾收集器的目的是回收堆中存在的无法访问的对象。当执行堆栈超过当前线程允许的限制时,发生堆栈溢出。根据定义,堆栈上的所有项都可以访问,因此垃圾收集器无法“清理”堆栈
答案 2 :(得分:2)
在软件中,当在调用堆栈上使用所有内存时会发生堆栈溢出,因此无法再调用任何方法。
答案 3 :(得分:1)
StackOverflowException! 执行堆栈溢出时抛出的异常,因为它包含太多嵌套方法调用。
基本上从.NET Framework 2.0开始,try-catch块无法捕获StackOverflowException对象,默认情况下会终止相应的进程。因此,建议用户编写代码以检测并防止堆栈溢出。例如,如果您的应用程序依赖于递归,请使用计数器或状态条件来终止递归循环。
答案 4 :(得分:1)
.NET仍然使用堆栈来分配局部变量(以及堆栈帧的其余部分),因此它与它总是具有相同的意义。唯一的区别是它抛出一个异常,可以在2.0以下的.NET版本中捕获。但是,编写能够正确恢复此条件的代码具有挑战性。因此,当前版本不再允许您捕获它。但是,堆栈溢出不会在任何.NET版本中导致未定义的行为。
答案 5 :(得分:1)
它与.NET几乎没有关系,堆栈是处理器的实现细节。几乎任何编程语言都需要处理它以获得可接受的性能,它通常会影响语言的设计。
处理器堆栈支持的第一件事是调用子例程。 CALL指令将指令指针的值压入堆栈并跳转到一大块代码。完成RET指令后,它会将指令指针值从堆栈中弹回,并在CALL指令后的指令处继续执行。您将在.NET语言中将其识别为方法。
子程序通常需要处理从调用代码传递的变量。在处理器级别,这通过使用PUSH指令在堆栈上推送它们的值来工作。然后,被调用的子程序通过在一个众所周知的位置索引堆栈来读取它们的值。您将在.NET语言中将其识别为方法参数。
子程序通常需要一些内存来存储中间值。获得一些便宜的方法是调整堆栈以在其上创建一些空间。您会将此识别为方法的局部变量。
如您所见,.NET中的任何方法调用都会占用堆栈中的一些空间。存储返回地址,方法参数和局部变量。它是一种有限的资源,但是,处理器堆栈在32位操作系统上可以增长到1兆字节。默认值,技术上可以要求更多空间。
当方法调用另一个调用另一个方法的方法时会出现问题,等等。每种方法占用空间。这无法永远持续下去,最终处理器耗尽了堆栈空间。这就是.NET中的Big Kaboom,StackOverflowException。其核心是低级操作系统故障。从SOE中恢复是不可能的,代码在处理器上运行的主要机制是错误的。你不能抓住异常,你的程序会立即死亡。
答案 6 :(得分:0)
.NET中常见的错误是:
private int someProperty;
public int SomeProperty
{
get { return SomeProperty; }
set { SomeProperty = value; }
}
这会给你一个StackOverflowException。唯一的线索是警告someProperty
从未使用过。