在 .Net 世界中,堆栈溢出实际上意味着什么?
在 .Net 垃圾收集世界中,堆栈溢出实际上意味着什么?
What does stack overflow actually mean in a .Net garbage collected world?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
在 .Net 垃圾收集世界中,堆栈溢出实际上意味着什么?
What does stack overflow actually mean in a .Net garbage collected world?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(7)
与其他地方的情况完全相同 - 你已经毁掉了堆栈,通常是因为你递归得很糟糕。例如:
现在这个例子可能通过尾递归进行优化,在这种情况下它将永远运行 - 但除此之外(在尾递归不可行的更一般情况下),这个将为每次递归调用 Foo 推送一个新的堆栈帧...并且这些堆栈帧永远不会被弹出,因为调用永远不会真正返回。
这与垃圾收集无关。
Exactly the same thing as it does everywhere else - you've blown the stack, usually because you've recursed badly. For example:
Now this example may get optimized with tail-recursion, in which case it will just run forever - but otherwise (and in a more general case where tail-recursion isn't feasible), this will push a new stack frame for each recursive call to
Foo
... and those stack frames will never be popped, as the calls will never actually return.This has nothing to do with garbage collection.
堆栈溢出和垃圾收集本质上是正交的概念:堆栈与堆。垃圾收集器的目的是回收堆中无法访问的对象。当执行堆栈超出当前线程允许的限制时,就会发生堆栈溢出。根据定义,堆栈上的所有项目都是可访问的,因此垃圾收集器无法“清理”堆栈
A stack overflow and garbage collection are essentially orthogonal concepts: stack vs. heap. The purpose of the garbage collector is to reclaim unreachable objects which live in the heap. A stack overflow occurs when the execution stack exceeds the limit allowed by the current thread. All items on the stack are by definition reachable hence the garbage collector can do nothing to "clean up" the stack
在软件中,当调用堆栈上的所有内存都被使用时,就会发生堆栈溢出,因此无法调用更多方法。
In software, a stack overflow occurs when all the memory is used on the call stack, and thus no more methods can be called.
堆栈溢出异常!
当执行堆栈溢出时抛出的异常,因为它包含太多嵌套方法调用。
基本上从.NET Framework 2.0开始,StackOverflowException对象无法被try-catch块捕获,并且默认情况下会终止相应的进程。因此,建议用户编写代码来检测和防止堆栈溢出。例如,如果您的应用程序依赖于递归,请使用计数器或状态条件来终止递归循环。
StackOverflowException!
The exception that is thrown when the execution stack overflows because it contains too many nested method calls.
Basically starting with the .NET Framework 2.0, a StackOverflowException object cannot be caught by a try-catch block and the corresponding process is terminated by default. Consequently, users are advised to write their code to detect and prevent a stack overflow. For example, if your application depends on recursion, use a counter or a state condition to terminate the recursive loop.
.NET 仍然使用堆栈来分配局部变量(以及堆栈帧的其余部分),因此它的含义与它始终具有的含义几乎相同。唯一的区别是它会抛出异常,该异常可以在 2.0 以下的 .NET 版本中捕获。然而,编写能够正确从这种情况中恢复的代码具有挑战性。因此,当前版本不再允许您捕获它。但是,堆栈溢出不会在任何版本的 .NET 中导致未定义的行为。
.NET still uses the stack to allocate local variables (and the rest of the stack frame), so it means pretty much the same thing it always has. The only difference is that it throws an exception, which can be caught in .NET versions below 2.0. However, it is challenging to write code that correctly recovers from this condition. Thus, current versions no longer allow you to catch it. However, stack overflows don't cause undefined behavior in any version of .NET.
.NET 中一个很常见的错误是这样的:
这会给你一个 StackOverflowException。唯一的线索是从未使用过
someProperty
的警告。Quite a common mistake in .NET is something like:
Which will give you a StackOverflowException. The only clue is a warning that
someProperty
is never used.它与.NET 关系不大,堆栈是处理器的实现细节。几乎任何编程语言都需要处理它以获得可接受的性能,它通常会极大地影响语言的设计。
处理器堆栈支持的第一件事是调用子例程。 CALL 指令将指令指针的值压入堆栈并跳转到代码块。它以 RET 指令完成,它将指令指针值从堆栈中弹出,并在 CALL 指令之后的指令处继续执行。您会将此视为 .NET 语言中的方法。
子例程通常需要使用从调用代码传递的变量。在处理器级别,这是通过使用 PUSH 指令将它们的值推送到堆栈上来实现的。然后,被调用的子例程通过在众所周知的位置索引堆栈来读取它们的值。您将把它视为 .NET 语言中的方法参数。
子例程通常需要一些内存来存储中间值。获得一些的廉价方法是调整堆栈以在其上创建一些空间。您会将其识别为方法的局部变量。
正如您所看到的,.NET 中的任何方法调用都会消耗堆栈中的一些空间。存储返回地址、方法参数和局部变量。然而,它是一种有限的资源,在 32 位操作系统上,处理器堆栈可以增长到 1 MB。默认值,技术上可以要求更多空间。
当一个方法调用另一个方法而另一个方法又调用另一个方法时,就会出现问题,等等。每个方法都占用空间。这不可能永远持续下去,最终处理器会耗尽堆栈空间。这就是 .NET 中的大爆炸,StackOverflowException。其核心是低级操作系统故障。从 SOE 中恢复是不可能的,在处理器上运行代码的主要机制是错误的。你无法捕获异常,你的程序会立即死亡。
It has little to do with .NET, the stack is an implementation detail of the processor. Just about any programming language needs to deal with it in order to get acceptable performance, it often affects the design of the language a great deal.
The very first thing the processor stack supports is calling subroutines. The CALL instruction pushes the value of the instruction pointer on the stack and jumps to a chunk of code. Which completes with the RET instruction, it pops the instruction pointer value back off the stack and execution continues where it left off, at the instruction after the CALL instruction. You'll recognize this as a method in .NET languages.
A subroutine often needs to work with variables passed from the calling code. At the processor level this works by pushing their value on the stack with the PUSH instruction. The called subroutine then reads their value by indexing the stack at a well-known location. You'll recognize this as a method parameter in .NET languages.
A subroutine often needs some memory to store intermediary values. A cheap way to get some is to adjust the stack to create some space on it. You'll recognize this as a method's local variables.
As you can see, any method call in .NET consumes some space from the stack. To store the return address, method arguments and local variables. It is a limited resource however, the processor stack can grow up to one megabytes on a 32-bit operating system. The default value, it is technically possible to ask for more space.
Trouble arises when a method calls another method which calls another method, etcetera. Each method occupies space. This cannot go on for ever, eventually the processor runs out of stack space. That's the Big Kaboom, StackOverflowException in .NET. At its core it is a low-level operating system fault. Recovering from an SOE is impossible, the primary mechanism by which code runs on a processor is faulty. You cannot catch the exception, your program dies an instant death.