python内存错误(有足够的可用内存)

时间:2012-10-03 18:51:29

标签: python memory

我正在尝试计算文本文件中字符串的出现次数。 文本文件看起来像这样,每个文件大约200MB。

String1 30
String2 100
String3 23
String1 5
.....

我想将计数保存到字典中。

count  = {}
for filename in os.listdir(path):
    if(filename.endswith("idx")):
        continue
    print filename  
    f = open(os.path.join(path, filename))
    for line in f:
        (s, cnt) = line[:-1].split("\t")
        if(s not in count):
            try:
                count[s] = 0 
            except MemoryError:
                print(len(count))
                exit()
        count[s] += int(cnt)  
    f.close()
    print(len(count))

我在count[s] = 0遇到内存错误, 但我的电脑里还有更多的可用内存 我该如何解决这个问题? 谢谢!

更新: 我在这里复制了实际的代码。 我的python版本是2.4.3,并且该机器运行linux并且具有大约48G内存,但它仅消耗不到5G。代码在len(count)=44739243停止。

UPDATE2: 字符串可以重复(不是唯一的字符串),所以我想要添加字符串的所有计数。我想要的操作只是读取每个字符串的计数。每个文件大约有10M行,我有30多个文件。我预计这个数字还不到1000亿。

UPDATE3: 操作系统是linux 2.6.18。

3 个答案:

答案 0 :(得分:4)

cPython 2.4可能会出现大内存分配问题,即使在x64上也是如此:

$ python2.4 -c "'a' * (2**31-1)"
Traceback (most recent call last):
  File "<string>", line 1, in ?
MemoryError
$ python2.5 -c "'a' * (2**31-1)"
$

更新到最近的python解释器(如cPython 2.7)以解决这些问题,并确保安装64位版本的解释器。

如果字符串具有非平凡的大小(即长于示例中的&lt; 10字节),您可能还想简单地存储它们的哈希值,甚至使用概率(但更有效)存储,如{ {3}}。要存储它们的哈希值,请用

替换文件处理循环
import hashlib
# ...
for line in f:
    s, cnt = line[:-1].split("\t")
    idx = hashlib.md5(s).digest()
    count[idx] = count.get(idx, 0) + int(cnt)
# ...

答案 1 :(得分:1)

如果你要做的只是计算唯一字符串的数量,你可以通过散列每个字符串来大大减少你的内存占用:

    (s, cnt) = line[:-1].split("\t")
    s = hash(s)

答案 2 :(得分:1)

我不确定为什么会发生这种崩溃。你的字符串估计平均大小有多长?如果它们有点冗长,你应该考虑对它们进行散列,如已经建议的那样。缺点是,您放弃了列出唯一键的选项,只需检查数据中是否包含字符串。

关于已经达到5 GB的内存限制,可能与你过时的python版本有关。如果您可以选择更新,请获取2.7。相同的语法(加上一些额外的),没有问题。好吧,我甚至不知道下面的代码是否仍然与2.4兼容,也许你必须再次发出with语句,至少这是你在2.7中编写它的方式。

您的版本的主要区别是手动运行垃圾收集。另外,你可以提高python使用的内存限制。正如你所提到的,它只使用了一小部分实际内存,所以万一有一些奇怪的默认设置禁止它变大,试试这个:

MEMORY_MB_MAX = 30000
import gc
import os
import resource
from collections import defaultdict
resource.setrlimit(resource.RLIMIT_AS, (MEMORY_MB_MAX * 1048576L, -1L))

count  = defaultdict(int)
for filename in os.listdir(path):
    if(filename.endswith("idx")):
        continue
    print filename  
    with open(os.path.join(path, filename)) as f:
        for line in f:
            s, cnt = line[:-1].split("\t")
            count[s] += int(cnt)  
    print(len(count))
    gc.collect()

除此之外,我没有理解你的行s, cnt = line[:-1].split("\t"),尤其是[:-1]。如果文件看起来像你注意到的那样,那么这将删除你的数字的最后数字。这是故意的吗?