我正在从事一个项目,该项目涉及从保存在内存中的大列表中访问数据。因为列表非常庞大(数百万行),所以我密切关注正在使用的内存量。我使用 OS X,所以我在创建这些列表时保持活动监视器打开。
我注意到列表使用的内存量可能会根据其构造方式而有很大差异,但我似乎无法弄清楚原因。
现在看一些示例代码:
(我在 OSX 10.8.3 上使用 Python 2.7.4)
下面的第一个函数创建一个列表并用所有相同的三个随机数填充它。
下面的第二个函数创建一个列表并用所有不同的随机数填充它。
import random
import sys
def make_table1(size):
list1 = size *[(float(),float(),float())] # initialize the list
line = (random.random(),
random.random(),
random.random())
for count in xrange(0, size): # Now fill it
list1[count] = line
return list1
def make_table2(size):
list1 = size *[(float(),float(),float())] # initialize the list
for count in xrange(0, size): # Now fill it
list1[count] = (random.random(),
random.random(),
random.random())
return list1
(首先让我说,我意识到上面的代码可以写得更有效率。这样写是为了让两个示例尽可能相似。)
现在我使用这些函数创建一些列表:
In [2]: thing1 = make_table1(6000000)
In [3]: sys.getsizeof(thing1)
Out[3]: 48000072
此时我的内存使用量增加了大约 46 MB,这是我从上面给出的信息中所期望的。
现在为下一个功能:
In [4]: thing2 = make_table2(6000000)
In [5]: sys.getsizeof(thing2)
Out[5]: 48000072
可以看到,两个列表占用的内存是一样的。它们的长度完全相同,因此这是意料之中的。令我没想到的是,我的 Activity Monitor 使用的内存跃升到了 1 GB 以上!
我知道会有一些开销,但要多 20 倍?46MB 列表需要 1 GB?
严重地?
好吧,继续诊断...
我尝试的第一件事是收集任何垃圾:
In [5]: import gc
In [6]: gc.collect()
Out[6]: 0
它使使用的内存量为零。
接下来我使用 guppy 来查看内存的去向:
In [7]: from guppy import hpy
In [8]: hpy().heap()
Out[8]:
Partition of a set of 24217689 objects. Total size = 1039012560 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 6054789 25 484821768 47 484821768 47 tuple
1 18008261 74 432198264 42 917020032 88 float
2 2267 0 96847576 9 1013867608 98 list
3 99032 0 11392880 1 1025260488 99 str
4 585 0 1963224 0 1027223712 99 dict of module
5 1712 0 1799552 0 1029023264 99 dict (no owner)
6 13606 0 1741568 0 1030764832 99 types.CodeType
7 13355 0 1602600 0 1032367432 99 function
8 1494 0 1348088 0 1033715520 99 type
9 1494 0 1300752 0 1035016272 100 dict of type
<691 more rows. Type e.g. '_.more' to view.>
好吧,我的记忆被占用了:
462 MB 的元组(嗯?)
412 MB 的浮点数(什么?)
92 MB 的列表(好的,这个很有意义。2*46MB = 92)
我的列表是预先分配的,所以我认为没有过度分配。
问题:
为什么这两个非常相似的列表使用的内存量如此不同?
有没有不同的方法来填充没有太多开销的列表?
有没有办法释放所有的内存?
注意:请不要建议存储在磁盘上或使用 array.array 或 numpy 或 pandas 数据结构。这些都是不错的选择,但这个问题与它们无关。这个问题是关于普通的旧列表。
我用 Python 3.3 尝试过类似的代码,结果是一样的。
这是有类似问题的人。它包含一些提示,但不是同一个问题。
谢谢你们!