3

我刚刚使用 Windows 7 64 位和 Python 2.7 32 位在 python 控制台中得到以下输出:

>>> a = {}
>>> for k in xrange(1000000): a[k] = k
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError
>>> len(a)
21846
>>> a[21846]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 21846
>>> a[21846] = 21846
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError

为什么我不能在 dict 中添加超过 21846 个项目?

这个数字(2 * 3 * 11 * 331)有什么特别之处?

注意:我的 RAM 比 sys.getsizeof(a) == 393356 多得多

4

3 回答 3

8

非常令人惊讶!Python 21846 没有什么特别之处。下面是 32 位 Windows Vista 和 Python 2.7.5 和适中的 3 GB RAM:

>>> a = {}
>>> for k in xrange(1000000): a[k] = k
...
>>> len(a)
1000000

1000万也可以做到。但这是从一个新的控制台窗口开始的。也许您在向我们展示之前做了其他事情,并留下了一些大型数据结构?按照现代标准,21846 很小;-)

之后

这已发布到 Python 的错误跟踪器,在这里: http ://bugs.python.org/issue19246

它被关闭为“不会修复”,因为同样的行为被一个简单的纯 C 程序重现。也就是说,系统 Cmalloc()free()应该受到责备,而理智的 Python 对此无能为力。它似乎是特定于 Windows 的,使用 Microsoft 的 C 库。问题是堆碎片:在分配和释放许多不同大小的对象后,系统malloc()在被要求提供“较大”内存块时失败,尽管有大量可用字节可用。但它们不在一个连续的块中(至少不在系统malloc()识别的一个块中)。

事情发生了;-)

于 2013-10-10T04:43:44.133 回答
2

那是您环境的约束,与 Python 字典无关。因此,您的问题的答案是:python 字典可以容纳您的环境允许的尽可能多的内容。

于 2013-10-10T04:36:24.150 回答
2

正如@Leifingson 和@Tim 提到的,这种行为取决于之前所做的事情。为了说明内存消耗,我将使用以下示例:

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a = {}
>>> for k in xrange(1000000): a['a' * k] = k
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError
>>> len(a)
64036

如果我们采用摘要键长度:

>>> log(sum(xrange(64036)), 2)
30.93316861532543

我们将接近整数溢出!

棘手的部分是:

>>> import sys
>>> sys.getsizeof(a)
1573004

sys.getsizeof返回大小不同的东西,然后是所有的 dict 项目。也许它是所有关键哈希的大小,我不知道。

做完之后,

>>> a = {}

将释放所有 2 Gb 的已分配内存,但会使 GC(我责怪它)处于某种瘫痪状态。所以执行:

>>> for k in xrange(1000000): a[k] = k

将造成:

MemoryError

并且类似于询问的“魔术”数字:

>>> len(a)
87382
于 2013-10-10T05:11:10.237 回答