为啥增加递归深度会导致堆栈溢出错误?

Posted

技术标签:

【中文标题】为啥增加递归深度会导致堆栈溢出错误?【英文标题】:Why does increasing the recursion depth result in stack overflow error?为什么增加递归深度会导致堆栈溢出错误? 【发布时间】:2021-01-26 23:56:29 【问题描述】:

我定义一个无限递归函数为:

>>>def f():
>>>   f()
>>>

然后我调用了这个函数,这发生了:

>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
File "<stdin>", line 2, in f
File "<stdin>", line 2, in f
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
>>>

接下来我会这样做:

>>>import sys
>>>sys.getrecursionlimit()
1000
>>>sys.setrecursionlimit(2147483647) #as 2147483647 is the highest number I can set for recursion in Python 3.8.5

然后我再次调用该函数,但是...

>>> f()
Traceback (most recent call last):
File "<stdin>", line 2, in f
File "<stdin>", line 2, in f
File "<stdin>", line 2, in f
[Previous line repeated 997 more times]
MemoryError: Stack overflow

我想知道,在将递归限制更改为 2147483647 之后,为什么 Python 仍然将递归限制为 1000?

【问题讨论】:

你能看到两次错误的不同吗?这意味着递归已更改为你定义的方式 第二个错误不是递归错误。这是内存错误。您应该运行您的程序并检查任务管理器(在 Windows 上),或者通常您的内存使用情况。也许你没有安装足够的内存。 好的,我看到了 @ Yeshwin Verma 程序员,谢谢 我有 8 GB RAM。 1000次递归树够吗? @Foxcric @ChristopherPeisert 一致认为标签属于标题(在常规句子中提及技术是可以的)。 【参考方案1】:

递归限制已成功更新,因为第一条错误消息是:

RecursionError: maximum recursion depth exceeded

然后在增加递归深度后,错误信息变为:

MemoryError: Stack overflow

来自sys.setrecursionlimit()上的文档:

将 Python 解释器堆栈的最大深度设置为限制。此限制可防止无限递归导致 C 堆栈溢出和 Python 崩溃。

因此,通过增加递归限制,程序使 Python 解释器崩溃。

由于 Python 没有优化尾递归,无限递归会导致堆栈溢出(内存不足)。

在许多现实世界的应用程序中,有必要在没有递归的情况下实现函数以避免堆栈溢出内存错误。

另见:Setting stacksize in a python script

【讨论】:

非常感谢@Christopher Peisert。现在我完全明白了。 但是我可以访问由于大递归而溢出的堆栈并更改它的大小吗? @DebtanuGupta:实际的底层堆栈是有限的,因为计算机是有限的。一个无限递归的函数总是会破坏堆栈;您尝试做的事情毫无意义。 @ShadowRanger 起初我对这两个错误感到困惑,一个是 RecursionError,另一个是 MemoryError。现在我看到了,堆栈的大小只能容纳 1000 次递归的数据。所以我想知道是否有任何方法可以增加堆栈大小以使递归超过 1000 次。 @DebtanuGupta:我怀疑它会超过 1000,它可能只是将回溯修剪到最近的 1000。如果我尝试做你所做的,Python 就会崩溃,因为它会破坏 C 堆栈;我什至没有得到MemoryError,只是一次硬崩溃(分段错误)。您的设置不同,但我不能确定如何确定(我在 WSLv2 下的 Alpine Linux 中运行 ipython)。

以上是关于为啥增加递归深度会导致堆栈溢出错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥无限递归会导致段错误

堆栈溢出一般是由啥原因导致的?

为啥我的代码会导致 java 中的堆栈溢出错误?它最终应该终止。 for循环版本不会导致错误

为啥这个 BigInteger 值会导致堆栈溢出异常? C#

为啥带有 setTimeout 的函数不会导致堆栈溢出

由于递归方法调用导致 Java 堆栈溢出