3

GAE 有各种限制,其中之一是最大可分配内存块的大小为 1Mb(现在增加了 10 倍,但这并没有改变问题)。限制意味着不能在 list() 中放置超过一定数量的项目,因为 CPython 会尝试为元素指针分配连续的内存块。拥有巨大的 list()s 可能被认为是不好的编程习惯,但即使程序本身没有创建巨大的结构,CPython 也会在幕后维护一些。

似乎 CPython 正在维护单个全局对象列表或其他东西。即具有许多小对象的应用程序倾向于分配越来越大的单个内存块。

第一个想法是 gc,禁用它会稍微改变应用程序的行为,但仍然保留了一些结构。

遇到此问题的最简单的简短应用程序是:

a = b = []
number_of_lists = 8000000
for i in xrange(number_of_lists):
    b.append([])
    b = b[0]

谁能告诉我如何防止 CPython 在应用程序中有许多对象时分配巨大的内部结构?

4

4 回答 4

8

在 32 位系统上,您创建的 8000000 个列表中的每一个都将为列表对象本身分配 20 个字节,再加上 16 个字节用于列表元素的向量。因此,您尝试分配至少 (20+16) * 8000000 = 20168000000 字节,大约 20 GB。这是最好的情况,如果系统 malloc 只分配与请求一样多的内存。

我计算了列表对象的大小如下:

  • 2PyListObject结构本身的指针(见listobject.h
  • 1 个指针和一个Py_ssize_t用于PyObject_HEAD列表对象的一部分(参见object.h
  • 一个Py_ssize_t用于PyObject_VAR_HEAD(也在 object.h 中)

列表元素的向量略微过度分配,以避免在每次追加时调整它的大小 - 请参阅listobject.c中的 list_resize 。大小为 0、4、8、16、25、35、46、58、72、88,......因此,您的单元素列表将为 4 个元素分配空间。

你的数据结构是一个有点病态的例子,付出了可变大小的列表对象的代价而不使用它——你所有的列表只有一个元素。您可以通过使用元组而不是列表来避免 12 字节的过度分配,但为了进一步减少内存消耗,您将不得不使用使用更少对象的不同数据结构。很难更具体,因为我不知道您要完成什么。

于 2009-02-21T13:43:26.293 回答
0

我对你在问什么感到有点困惑。在该代码示例中,不应该对任何内容进行垃圾收集,因为您实际上从未杀死任何引用。您在 a 中保存对顶级列表的引用,并且在其中添加嵌套列表(在每次迭代时保存在 b 中)。如果您删除“a =”,那么您将获得未引用的对象。

编辑:作为对第一部分的回应,是的,Python 拥有一个对象列表,因此它可以知道要剔除什么。这是整个问题吗?如果没有,请评论/编辑您的问题,我会尽力帮助填补空白。

于 2009-02-21T10:47:02.053 回答
0

你想用

a = b = []

b = b[0]

陈述?在 Python 中看到这样的语句当然很奇怪,因为它们不会做你可能天真的期望的事情:在那个例子中,a并且b同一个列表的两个名称(想想 C 中的指针)。如果你正在做很多这样的操作,很容易混淆垃圾收集器(和你自己!),因为你有很多奇怪的引用漂浮在周围,没有被正确清除。

如果不知道为什么要执行它看起来正在做的事情,就很难诊断出该代码出了什么问题。当然,它暴露了一些解释器的怪异......但我猜你正在以一种奇怪的方式处理你的问题,而更 Pythonic 的方法可能会产生更好的结果。

于 2009-02-21T11:20:33.237 回答
0

为了让您意识到这一点,Python 有自己的分配器。您可以在配置步骤中使用 --without-pyalloc 禁用它。

但是,最大的 arena 是 256KB,所以这不应该是问题。您还可以使用 --with-pydebug 在启用调试的情况下编译 Python。这将为您提供有关内存使用的更多信息。

我怀疑你的预感,并且确信 oefe 的诊断是正确的。列表使用连续内存,因此如果您的列表对于系统领域来说太大了,那么您就不走运了。如果您真的很喜欢冒险,您可以重新实现 PyList 以使用多个块,但这将是很多工作,因为 Python 的各个位都需要连续的数据。

于 2009-02-21T17:43:43.580 回答