4

我编写了一个读取两个文件内容的python脚本,第一个是一个相对较小的文件(~30KB),第二个是一个较大的文件~270MB。两个文件的内容都加载到字典数据结构中。加载第二个文件时,我预计所需的 RAM 量大致相当于磁盘上文件的大小,可能会有一些开销,但观察我 PC 上的 RAM 使用情况,它似乎始终需要 ~2GB(大约文件大小的 8 倍)。相关的源代码如下(插入暂停以便我可以看到每个阶段的 RAM 使用情况)。消耗大量内存的行是“tweets = map(json.loads, tweet_file)”:

def get_scores(term_file):
    global scores
    for line in term_file:
        term, score  = line.split("\t") #tab character
        scores[term] = int(score)

def pause():
    tmp = raw_input('press any key to continue: ')

def main():
    # get terms and their scores..
    print 'open word list file ...'
    term_file = open(sys.argv[1])
    pause()
    print 'create dictionary from word list file ...'
    get_scores(term_file)
    pause()
    print 'close word list file ...'
    term_file.close
    pause()

    # get tweets from file...
    print 'open tweets file ...'
    tweet_file = open(sys.argv[2])
    pause()
    print 'create dictionary from word list file ...'
    tweets = map(json.loads, tweet_file) #creates a list of dictionaries (one per tweet)
    pause()
    print 'close tweets file ...'
    tweet_file.close
    pause()

有人知道为什么吗?我担心的是我想将我的研究扩展到更大的文件,但会很快耗尽内存。有趣的是,打开文件后内存使用量似乎没有明显增加(因为我认为这只是创建了一个指针)。

我有一个想法,尝试一次遍历文件一行并处理我能做的事情,只存储我需要的最小值以供将来参考,而不是将所有内容加载到字典列表中,但我只是想看看是否创建字典时文件大小到内存的乘数大约是 8 倍是否符合其他人的经验?

4

3 回答 3

2

我的猜测是您的字典中有多个副本同时存储在内存中(以各种格式)。例如,该行:

tweets = map(json.loads, tweet_file) #creates a list of dictionaries (one per tweet)

将创建一个副本(+400~1000MB,包括字典开销)。但你原来tweet_file留在记忆中。为什么会有这么大的数字?好吧,如果您使用 Unicode 字符串,每个 Unicode 字符在内存中使用 2 或 4 个字节。而在您的文件中,假设 UTF-8 编码,大多数字符仅使用 1 个字节。如果您在 Python 2 中使用纯字符串,则内存中字符串的大小应该与磁盘上的大小几乎相同。因此,您将不得不找到其他解释。

编辑: Python 2 中“字符”占用的实际字节数可能会有所不同。下面是一些例子:

>>> import sys
>>> sys.getsizeof("")
40
>>> sys.getsizeof("a")
41
>>> sys.getsizeof("ab")
42

如您所见,似乎每个字符都被编码为一个字节。但:

>>> sys.getsizeof("à")
42

不适用于“法语”字符。和 ...

>>> sys.getsizeof("世")
43
>>> sys.getsizeof("世界")
46

对于日语,我们每个字符有 3 个字节。

上述结果取决于站点——并且可以通过我的系统使用“UTF-8”默认编码这一事实来解释。上面计算的“字符串大小”实际上是表示给定文本的“字节字符串大小”。

如果 'json.load' 使用“unicode”字符串,结果会有所不同:

>>> sys.getsizeof(u"")
52
>>> sys.getsizeof(u"a")
56
>>> sys.getsizeof(u"ab")
60
>>> sys.getsizeof(u"世")
56
>>> sys.getsizeof(u"世界")
60

在这种情况下,如您所见,每个额外的字符都会增加 4 个额外的字节。


也许文件对象会缓存一些数据?如果要触发对象的显式 dellaocation,请尝试将其引用设置为 None:

tweets = map(json.loads, tweet_file) #creates a list of dictionaries (one per tweet)
[...]
tweet_file.close()
tweet_file = None

当不再有对对象的任何引用时,Python 将释放它——并释放相应的内存(从 Python 堆中——我认为内存不会返回给系统)。

于 2013-06-26T07:05:47.507 回答
1

我写了一个快速测试脚本来确认你的结果......

import sys
import os
import json
import resource

def get_rss():
    return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * 1024

def getsizeof_r(obj):
    total = 0
    if isinstance(obj, list):
        for i in obj:
            total += getsizeof_r(i)
    elif isinstance(obj, dict):
        for k, v in obj.iteritems():
            total += getsizeof_r(k) + getsizeof_r(v)
    else:
        total += sys.getsizeof(obj)
    return total

def main():
    start_rss = get_rss()
    filename = 'foo'
    f = open(filename, 'r')
    l = map(json.loads, f)
    f.close()
    end_rss = get_rss()

    print 'File size is: %d' % os.path.getsize(filename)
    print 'Data size is: %d' % getsizeof_r(l)
    print 'RSS delta is: %d' % (end_rss - start_rss)

if __name__ == '__main__':
    main()

...打印...

File size is: 1060864
Data size is: 4313088
RSS delta is: 4722688

...所以我只得到了四倍的增长,这是因为每个 Unicode 字符占用了四个字节的 RAM。

也许你可以用这个脚本测试你的输入文件,因为我无法解释为什么你的脚本会增加八倍。

于 2013-06-26T08:48:31.727 回答
0

您是否考虑过键的内存使用情况?如果您的字典中有很多小值,则键的存储可能占主导地位。

于 2013-06-26T12:02:44.690 回答