首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Python中打开文件后释放内存

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

Stack Overflow用户
提问于 2012-09-14 06:39:07
回答 4查看 14.8K关注 0票数 17

我用Python打开一个3 GB的文件来读取字符串。然后,我将此数据存储在字典中。我的下一个目标是使用这个字典构建一个图表,这样我就可以密切监控内存使用情况。

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

代码语言:javascript
复制
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"

最后几行都在那里,这样我就可以监控内存使用情况。该脚本使用的内存略高于3 GB。清除字典可以释放大约300MB的空间。当脚本结束时,剩余的内存将被释放。

我正在使用Ubuntu,并且我已经在终端中使用了“系统监视器”和“空闲”命令来监控内存使用情况。

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

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

EDIT2 :我在Ubuntu12.04上运行Python2.7.3

EDIT3 :我意识到我忘了提到一些非常重要的事情。我真正的问题不是我的操作系统不能“取回”Python使用的内存。后来,Python似乎并没有重用这些内存(它只是要求操作系统有更多的内存)。

EN

回答 4

Stack Overflow用户

发布于 2012-09-14 08:02:33

这对我来说也没有任何意义,我想弄清楚这是如何/为什么发生的。(我认为这也应该是如何工作的!)我在我的机器上复制了它--尽管文件比较小。

我在这里看到了两个离散的问题

  1. 为什么Python要将文件读取到内存中(使用懒行读取,这是不应该的-对吧?)
  2. 为什么Python不释放内存给system

我对Python的内部结构一无所知,所以我只是做了大量的网络搜索。所有这些都可能是完全错误的。(我几乎不再开发,在过去的几年里一直在技术的商业方面)

懒惰的行读...

我环顾四周,发现了这个帖子-

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

它来自一个更早的python版本,但这句话引起了我的共鸣:

readlines()一次读取整个文件并按行拆分。

然后,我看到了这篇同样古老的effbot帖子:

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

关键的结论是:

例如,如果您有足够的内存,可以使用

方法将整个文件存储到内存中。

还有这个:

在Python2.2和更高版本中,您可以循环遍历文件对象本身。这相当于在幕后使用readline(N),但看起来要好得多

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

此方法返回与

(F)相同的内容,从2.3版开始就不再使用了:使用for line in file。

这让我觉得可能是有一些声音在作怪。

因此,如果我们查看readline[ 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,日志文件的大小,以前)

代码语言:javascript
复制
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)

我猜测我们并不是真的懒惰--使用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()。在Python2.5之前,arenas (256KB内存块)从未被释放。现在,一些应用程序的虚拟内存大小会有所下降,特别是那些经常临时使用大量小对象的长时间运行的应用程序。请注意,当Python将竞技场返回给平台C的free()时,不能保证平台C库反过来会将该内存返回给操作系统。该补丁的效果是停止不可能,在测试中,它似乎是有效的,至少在基于微软C和gcc的系统上。感谢Evan Jones的辛勤工作和耐心。因此,对于linux下的2.4 (正如您测试的那样),对于收集的大量小对象,您确实不会总是拿回使用的内存。

因此(我认为)你会看到f.read()和f.readlines()的不同之处在于,前者将整个文件作为一个大的string对象(即不是一个小对象)读取,而后者返回一个行列表,其中每行都是一个python对象。

如果“for line in data:”构造本质上是包装readlines而不是readline,也许这与它有关?也许这不是拥有一个3 3GB对象的问题,而是拥有数百万个30k对象的问题。

票数 17
EN

Stack Overflow用户

发布于 2012-09-14 10:51:04

你尝试的是哪个版本的python?

我在Python2.7/Win7上做了一个测试,它像预期的那样工作,内存被释放了。

我在这里生成与您类似的示例数据:

代码语言:javascript
复制
import random

fn = random.randint

with open('ips.txt', 'w') as f: 
    for i in xrange(9000000):
        f.write('{0}.{1}.{2}.{3} username-{4}\n'.format(
            fn(0,255),
            fn(0,255),
            fn(0,255),
            fn(0,255),
            fn(0, 9000000),
        ))

然后是你的脚本。我用defaultdict替换了dict,因为抛出异常会使代码变慢:

代码语言:javascript
复制
import time
from collections import defaultdict

def read_file(filename):
    with open(filename) as data:

        accounts = defaultdict(set)

        for line in data:
            IP, username = line.split()[:2]
            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"

if __name__ == '__main__':
    read_file('ips.txt')

正如您所看到的,内存达到1.4G,然后被释放,剩下36MB:

使用你的原始脚本,我得到了相同的结果,但速度慢了一点:

票数 4
EN

Stack Overflow用户

发布于 2012-09-14 10:04:46

Python释放内存供Python重用的时间与将内存释放回操作系统的时间是不同的。Python为某些类型的对象提供了内部池,它将自己重用这些池,但不会将其返回给操作系统。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12415783

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档