这对我来说也没有任何意义,我想弄清楚这是如何/为什么会发生的。(我认为这也应该是这样工作的!)我在我的机器上复制了它——尽管文件更小。
我在这里看到了两个离散的问题
- 为什么 Python 将文件读入内存(使用惰性行读取,它不应该 - 对吗?)
- 为什么 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 文档 [ 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)
我的猜测是,我们并没有真正懒惰地阅读带有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
free()
"""- 补丁 #1123430:Python 的小对象分配器现在会在一个 arena 中的所有内存再次未使用时返回一个 arena 给系统。在 Python 2.5 之前,arenas(256KB 内存块)从未被释放。一些应用程序会看到现在虚拟内存大小下降,尤其是长时间运行的应用程序,有时会临时使用大量小对象。请注意,当 Python 将竞技场返回到平台 Cfree()
时,不能保证平台 C 库将反过来将该内存返回给操作系统。补丁的效果是停止使这成为不可能,并且在测试中它似乎至少在基于 Microsoft C 和 gcc 的系统上有效。感谢 Evan Jones 的辛勤工作和耐心.
因此,对于 linux 下的 2.4(如您所测试的那样),您确实不会总是取回已使用的内存,因为要收集大量的小对象。
因此(我认为)您在执行 f.read() 和 f.readlines() 之间看到的区别在于前者将整个文件作为一个大字符串对象(即不是一个小对象)读取,而后者则返回行列表,其中每一行都是一个 python 对象。
如果 'for line in data:' 构造本质上是 wrappingreadlines
而不是readline
,也许这与它有关?也许拥有单个 3GB 对象不是问题,而是拥有数百万个 30k 对象。