【问题标题】:How to cause stack overflow and heap overflow in python如何在python中导致堆栈溢出和堆溢出
【发布时间】:2012-09-18 15:24:14
【问题描述】:

我正在尝试了解 python 如何管理堆栈和堆。所以我想做一些“糟糕”的编程并导致堆栈溢出和堆溢出。我不明白为什么字符串例如进入堆栈而所有其他字符串进入堆。仅仅是设计师的约定吗?这些例子是否正确? 从我读过的内容来看,python 中的所有内容都是在堆中生成的,因为它是面向对象的,对吧?

已编辑:我认为像 C 这样的语言中的堆栈具有固定长度,但在 python 中,即使堆栈是动态分配的,正如 Anycorn 在他的回答中所说的那样。这就是为什么如果我同时尝试大字符串(在堆栈上)或列表(在堆上),我也会获得完整的内存。如果我错了,请纠正我。谢谢

来自http://docs.python.org/c-api/memory.html

Python 中的内存管理涉及一个私有堆,其中包含所有 Python 对象和数据结构。这个私人的管理 堆由 Python 内存管理器在内部确保。蟒蛇 内存管理器有不同的组件来处理各种 动态存储管理方面,例如共享、分段、 预分配或缓存。

在最低级别,原始内存分配器确保存在 私有堆中有足够的空间来存储所有与 Python 相关的数据 与操作系统的内存管理器交互。在之上 原始内存分配器,几个特定于对象的分配器操作 在同一个堆上并实现不同的内存管理策略 适应每种对象类型的特性。

这里有一些例子。您可以将它们复制粘贴到Python official visualizer 但较小的值会导致它无法运行...

堆栈溢出:

import time
word = "test "
x = word*1000000000
time.sleep(10)
print ("this message wont appear if stack overflow has occurred!") 

我明白了

x = word*1000000000
MemoryError

如果我删除一个零,它就会运行。当我使用x = word*500000000 时,我得到了最大内存使用量 所以我不能使堆栈溢出,因为即使堆栈是动态分配的?

对于堆溢出:

i = 10000
test_list = [0]
while i > 0 :
    test_list [:0] = test_list #insert a copy of itself at the beginning
    i -= 1

现在我不明白垃圾收集器是如何在程序中运行的。它是否在堆栈和堆上运行,因为它们都是动态分配的?是因为操作系统内存管理器吗?这些东西告诉我们关于 python 编程语言的特征的什么? 这是否证明了“动态语言”或“解释”一词的合理性? 很抱歉这个问题很长,但我只需要澄清一些事情。 提前致谢!

已编辑
我找到了我要找的东西: 如果您调用,您可能会导致“真正的”堆栈溢出 sys.setrecursionlimit(N) 的 N 值大于您的系统实际可以处理的值,然后尝试递归到该深度。在某些时候,您的系统会耗尽堆栈空间,Python 解释器会崩溃。

【问题讨论】:

  • 字符串对象也在堆上;可视化工具在这方面有点误导。
  • 您的第一个示例导致内存错误,而不是堆栈溢出。
  • @GeoPapas 我正在使用与您相同的可视化工具。此外,较低的值不会发生 stackoverflow,尤其是因为没有堆栈增长。
  • 我想你误解了什么是stackoverflow。
  • @GeoPapas 你追求的是哪个stackoverflow?耗尽堆栈内存?还是溢出堆栈缓冲区?

标签: python python-3.x heap-memory stack-overflow


【解决方案1】:

如有错误请指正:

据我所知,当涉及到实际的堆栈实现时,python 堆栈(在默认分布中)实际上是基于堆内存(使用malloc 分配的内存)。所以你不能导致堆栈溢出,但你可能会耗尽内存。您看到的计算机速度变慢是因为内存正在交换到磁盘,过程非常缓慢。

一般来说,你不知道解释/字节编译语言是如何实现它的堆栈的,但最喜欢它没有在堆栈内存中实现,所以你不会导致堆栈溢出。可以使用alloca 实现 Python,但为什么呢?

参照。 CPython - Internally, what is stored on the stack and heap?

尝试使用编译为机器码的编译语言、C++、Fortran 等进行相同的实验。

【讨论】:

  • 所以我们实际上有一个堆,其中实现了堆栈和堆? :o
  • @GeoPapas 在涉及非编译语言时,您必须区分程序调用堆栈和实际的机器内存堆栈
  • 好的,我想我正在清理这里。因此,与我们在 Python 中具有固定长度的 C 中的经典堆栈相比,即使堆栈被视为堆,这意味着它也是动态分配的?那是对的吗?提前致谢。等待回复澄清事情:D
  • “你不能导致堆栈溢出,但你可以耗尽内存。” -> 不正确。 Python 中的函数调用调用 C 中的 callfunction(),它递归评估器……下一次递归将看到另一个 callfunction() 堆积在 C 堆栈上。还有no platform independent way 用于标准C 程序来检测限制。 Python 只是祈祷并希望 setrecursionlimit() 足够低以避免堆栈溢出,但它可能不是。
【解决方案2】:

通过构建无限递归函数,您可以在 python 中很容易地导致堆栈溢出,就像在任何其他语言中一样。这在 python 中更容易,因为它实际上除了递归之外根本不需要做任何事情。

>>> def foo():
...     return foo()
... 

>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  .......
  File "<stdin>", line 2, in foo
RuntimeError: maximum recursion depth exceeded
>>> 

至于堆,它由垃圾收集器管理。您可以分配大量对象并最终用完堆空间,Python 将引发MemoryError,但这需要相当长的时间。您实际上是在问题中的“堆栈溢出”示例中做到了这一点。您在堆栈上存储了对字符串的引用,该字符串占用了进程可用的所有空闲内存。根据经验,Python 会在堆栈上存储对任何无法保证其大小的值的堆结构的引用。

至于它是如何工作的,从第一个例子中你可以看到 python 有一个内置的限制 它不会超过的调用堆栈的深度。然而,可用于堆空间的内存量由操作系统定义,并且取决于许多因素。

这些应该是 python 文档的适当部分,用于获取有关错误本身的信息:

【讨论】:

  • 那么如果我们创建一个大字符串,它也会存储在堆中吗?我认为 strigns 存储在堆栈中,这就是不可变的原因之一。我已经在 python 可视化器中看到,如果你创建字符串,它们被放置在意味着堆栈的框架中。嗯
  • 是也不是,尽管 python 字符串是不可变的序列,但没有特定的存储方式。您不能将它们存储在堆栈框架中,并且必须将它们存储为引用,否则您将无法从方法返回字符串,因为当函数返回时,它的数据将超出范围。 (这是一种简化。有很多方法可以实现 Python 解释器)。
  • 我认为这是字符串类型如何实现和python解释器如何将其呈现给程序之间的一个令人困惑的灰色地带。
  • 在这么多答案之后,我仍然看不到堆栈中存储了什么,什么没有。 :( 仍然使用递归示例,您无法实现堆栈溢出,因为它没有运行它只会抛出错误并且内存未更改:o。总而言之..只有名称存储在堆栈中,或者它们也是堆中的对象?
  • 这取决于实现,但典型的做法是在函数堆栈框架中为每个局部变量存储一个小结构。这个帧是在堆上分配的。然后,此结构的内容可以包含值的所有内容,例如整数,或者对包含其余部分的其他存储的引用。这些将存储在堆上。字符串在 CPython 中的存储方式略有不同,因此它必须执行更少的副本,但它们的内存仍然来自堆。
猜你喜欢
  • 2015-05-21
  • 2014-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 2010-09-11
相关资源
最近更新 更多