如何在Python中打开文件后释放内存

Pie*_*nne 14 python memory file-io large-files

我在Python中打开一个3 GB的文件来读取字符串.然后我将这些数据存储在字典中.我的下一个目标是使用这个字典构建一个图形,所以我正在密切监视内存使用情况.

在我看来,Python将整个3 GB文件加载到内存中,我无法摆脱它.我的代码看起来像这样:

with open(filename) as data:

    accounts = dict()

    for line in data:
        username = line.split()[1]
        IP = line.split()[0]

        try:
            accounts[username].add(IP)
        except KeyError:
            accounts[username] = set()
            accounts[username].add(IP)

print "The accounts will be deleted from memory in 5 seconds"
time.sleep(5)
accounts.clear()

print "The accounts have been deleted from memory"
time.sleep(5)

print "End of script"
Run Code Online (Sandbox Code Playgroud)

最后一行是在那里,以便我可以监视内存使用情况.该脚本在内存中使用超过3 GB的位.清除字典可以释放大约300 MB.当脚本结束时,释放剩余的内存.

我正在使用Ubuntu,我使用"系统监视器"和终端中的"免费"命令监视内存使用情况.

我不明白的是为什么在我清除字典之后Python需要这么多内存.文件是否仍存储在内存中?如果是这样,我怎么能摆脱它呢?我的操作系统没有看到释放内存的问题吗?

编辑:我已经尝试在清除字典后强行执行gc.collect(),但无济于事.

EDIT2:我在Ubuntu 12.04.LTS上运行Python 2.7.3

EDIT3:我意识到我忘了提到一些非常重要的东西.我真正的问题不在于我的操作系统没有"取回"Python使用的内存.后来,Python似乎没有重用那个内存(它只是要求操作系统有更多的内存).

Jon*_*sco 13

这对我来说确实没有意义,我想弄清楚这是怎么回事.(我认为这也应该如何工作!)我在我的机器上复制它 - 虽然文件较小.

我在这看到两个不连续的问题

  1. 为什么Python将文件读入内存(使用惰性线读取,它不应该 - 对吧?)
  2. 为什么不是Python释放内存到系统

我对Python内部结构一点也不了解,所以我只是进行了大量的网络搜索.所有这些都可能完全不合适.(我几乎没有发展,过去几年一直在科技的商业方面)

懒读线......

我环顾四周,找到了这篇文章 -

http://www.peterbe.com/plog/blogitem-040312-1

它来自python的早期版本,但这条线路与我共鸣:

readlines()立即读入整个文件并按行拆分.

然后我看到了这个,也是旧的,effbot帖子:

http://effbot.org/zone/readline-performance.htm

关键的一点是:

例如,如果您有足够的内存,则可以使用readlines方法将整个文件粘贴到内存中.

还有这个:

在Python 2.2及更高版本中,您可以遍历文件对象本身.这与封面上的readlines(N)非常相似,但看起来要好得多

查看用于xreadlines的pythons docs [ http://docs.python.org/library/stdtypes.html?highlight=readline#file.xreadlines ]:

此方法返回与iter相同的内容(f)从版本2.3开始不推荐使用:用于替换文件.

它让我觉得也许正在进行一些诽谤.

所以如果我们看一下readlines [ http://docs.python.org/library/stdtypes.html?highlight=readline#file.readlines ] ......

使用readline()读取EOF并返回包含如此读取的行的列表.

这看起来就像是在这里发生的事情.

然而,readline看起来像我们想要的[ http://docs.python.org/library/stdtypes.html?highlight=readline#file.readline ]

从文件中读取整行

所以我尝试将其切换到readline,并且该过程从未超过40MB(它增长到200MB,日志文件的大小,之前)

accounts = dict()
data= open(filename)
for line in data.readline():
    info = line.split("LOG:")
    if len(info) == 2 :
        ( a , b ) = info
        try:
            accounts[a].add(True)
        except KeyError:
            accounts[a] = set()
            accounts[a].add(True)
Run Code Online (Sandbox Code Playgroud)

我的猜测是,我们并不是真的懒得用结构读取文件for x in data- 尽管所有的文档和stackoverflow评论都表明我们是. readline()为我消耗的内存显着减少,并realdlines消耗了大约相同的内存量for line in data

释放记忆

在释放内存方面,我对Python的内部结构并不熟悉,但我回想起当我使用mod_perl时......如果我打开了一个500MB的文件,那个apache的孩子就会变得那么大.如果我释放了内存,它只会在那个孩子中自由 - 垃圾收集的内存从未返回到操作系统,直到进程退出.

所以我讨论了这个想法,发现了一些暗示这可能发生的链接:

http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm

如果你创建一个大对象并再次删除它,Python可能已经释放了内存,但所涉及的内存分配器不一定会将内存返回给操作系统,所以看起来好像Python进程使用了​​更多的虚拟内存而不是它实际使用.

这有点旧了,之后我发现了一堆随机(接受)补丁到python中,表明行为已经改变了,你现在可以将内存返回给操作系统了(截至2005年,当大多数补丁被提交并且显然被批准时) ).

然后我发现这个帖子http://objectmix.com/python/17293-python-memory-handling.html - 并注意到评论#4

"" - 补丁#1123430:Python的小对象分配器现在在竞技场中的free()所有内存再次未使用时返回系统的竞技场.在Python 2.5之前,竞技场(256KB内存块)从未被释放.有些应用程序会看到现在虚拟内存大小下降,特别是长时间运行的应用程序,有时会临时使用大量小对象.请注意,当Python返回平台C的竞技场时free(),无法保证平台C库将会反过来将该内存返回给操作系统.补丁的作用是停止使这不可能,并且在测试中它至少在Microsoft C和基于gcc的系统上似乎是有效的.感谢Evan Jones的辛勤工作和耐心.

因此,在Linux下使用2.4(正如您测试的那样),对于收集的大量小对象,您确实不会总是得到用过的内存.

因此(我认为)在执行f.read()和f.readlines()之间的区别在于前者将整个文件作为一个大字符串对象(即不是小对象)读取,而后者返回每行都是python对象的行列表.

如果'for line in data:'构造实际上是包装readlines而不是readline,那么这可能与它有关吗?也许这不是一个3GB对象的问题,而是拥有数百万个30k对象.

  • `for data in data.readline():`迭代一行中的字符.说'换行'并不意味着`line`实际上包含一行.这与原始脚本完全不同. (5认同)
  • `for line in data`绝对是懒惰的.如果您完全删除了字典,您会注意到该过程不会占用太多内存(因为您从不存储超过1行). (3认同)

Eli*_*ria 0

gc 模块可能很有用,尤其是该collect函数。我自己从未使用过它,但从文档来看,它看起来可能很有用。gc.collect()在你跑步之前我会尝试跑步accounts.clear()