2009-02-21 97 views
3

GAE有各种限制,其中一个是最大可分配内存块大小为1Mb(现在是10倍以上,但这不会改变问题)。限制意味着不能在list()中放置更多的项目数量,因为CPython会尝试为元素指针分配连续的内存块。拥有巨大的list()可以被认为是不好的编程习惯,但即使程序本身没有创建巨大的结构,CPython仍然在幕后。CPython内部结构

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

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

是遇到该问题

一个简单的短的应用是:

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

谁能赐教如何防止CPython的从具有许多应用对象时,巨大的分配内部结构?

+0

什么是**真正**问题。为什么耗尽内存很重要?你有什么具体问题似乎需要巨大的内存块?你可能可以解决这个问题,首先不分配内存。你试图解决的**真正**问题是什么? – 2009-02-21 12:52:32

回答

8

在32位系统上,您创建的每个8000000列表将为列表对象本身分配20个字节,并为列表元素向量分配16个字节。因此,您正在尝试分配至少(20 + 16)* 8000000 = 20168000000个字节,大约20 GB。这是最好的情况,如果系统malloc只按照要求分配尽可能多的内存。

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

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

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

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

0

我对你在问什么有点困惑。在这个代码示例中,没有任何东西应该被垃圾收集,因为你从来没有真正关闭任何引用。你在a中持有对顶级列表的引用,并且你在里面添加了嵌套列表(在每次迭代中保存在b中)。如果你删除'a =',然后你有没有被引用的对象。

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

+0

我已经提出了一些有待澄清的问题背景。这有帮助吗? – myroslav 2009-02-21 11:25:53

0

什么是你想与

a = b = [] 

b = b[0] 

语句来实现?在Python中看到类似这样的陈述当然很奇怪,因为他们不会做你可能天真地期望的东西:在这个例子中,ab相同的列表中的两个名字(想一想C中的指针)。如果你做了很多这样的操作,很容易混淆垃圾收集器(和你自己!),因为你有很多奇怪的引用在周围浮动,没有被正确清除。

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

+0

我认为这只是一个问题的例子,要问一些基础的GC结构,但我也无法做到。 – 2009-02-21 11:21:27

0

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

然而,最大的竞技场是256KB,所以不应该是这个问题。您也可以使用--with-pydebug编译启用调试的Python。这会给你更多关于内存使用的信息。

我怀疑你的预感,并确定oefe的诊断是正确的。一个列表使用连续的内存,所以如果你的列表对于系统竞技场来说太大,那么你运气不好。如果你真的冒险,你可以重新实现PyList来使用多个块,但是这将是很多工作,因为Python的各个部分都需要连续的数据。