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

2024-05-15 00:33:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用Python打开一个3gb的文件来读取字符串。然后我把这些数据储存在字典里。我的下一个目标是使用这个字典构建一个图,这样我就可以密切监视内存使用情况。

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

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。清除字典可以释放大约300 MB的空间。当脚本结束时,剩余的内存被释放。

我在使用Ubuntu,我在终端中同时使用“System Monitor”和“free”命令来监控内存使用情况。

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

编辑:我试图在清除字典后强制执行gc.collect(),但没有效果。

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

伊迪丝3:我意识到我忘了提一些很重要的事情。我真正的问题不是我的操作系统没有“找回”Python使用的内存。稍后,Python似乎不会重用该内存(它只是要求操作系统有更多的内存)。


Tags: 文件the内存inipadddata字典
3条回答

Python释放内存以供Python重用与释放内存回OS之间存在差异。Python为某些类型的对象提供了内部池,它将重用这些对象本身,但不会将其返回给OS。

这对我来说也毫无意义,我想弄清楚这是怎么发生的。(我也这么认为!)我把它复制到了我的机器上——不过用的是一个小文件。

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

  1. 为什么Python要将文件读入内存(使用惰性行读取,它不应该是正确的?)
  2. 为什么Python不向系统释放内存

我根本不懂Python内部的知识,所以我只是做了大量的web搜索。所有这些都可能完全偏离目标。(我几乎不再发展,在过去的几年里一直在科技行业工作)

懒线阅读

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

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

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

readlines() reads in the whole file at once and splits it by line.

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

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

关键是:

For example, if you have enough memory, you can slurp the entire file into memory, using the readlines method.

而这个:

In Python 2.2 and later, you can loop over the file object itself. This works pretty much like readlines(N) under the covers, but looks much better

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

This method returns the same thing as iter(f) Deprecated since version 2.3: Use for line in file instead.

这让我觉得也许有人在说粗话。

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

Read until EOF using readline() and return a list containing the lines thus read.

好像就是这样。

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

Read one entire line from the file

所以我试着把它切换到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

If you create a large object and delete it again, Python has probably released the memory, but the memory allocators involved don’t necessarily return the memory to the operating system, so it may look as if the Python process uses a lot more virtual memory than it actually uses.

那有点旧了,后来我在python中发现了一堆随机(可接受的)补丁,这些补丁表明行为发生了变化,现在可以将内存返回到os(截至2005年,大多数补丁都已提交并得到了批准)。

然后我找到了这篇文章http://objectmix.com/python/17293-python-memory-handling.html——并注意到注释4

"""- Patch #1123430: Python's small-object allocator now returns an arena to the system free() when all memory within an arena becomes unused again. Prior to Python 2.5, arenas (256KB chunks of memory) were never freed. Some applications will see a drop in virtual memory size now, especially long-running applications that, from time to time, temporarily use a large number of small objects. Note that when Python returns an arena to the platform C's free(), there's no guarantee that the platform C library will in turn return that memory to the operating system. The effect of the patch is to stop making that impossible, and in tests it appears to be effective at least on Microsoft C and gcc-based systems. Thanks to Evan Jones for hard work and patience.

因此,在linux下使用2.4(正如您所测试的那样),您确实不会总是得到 对于许多小对象来说 收集。

因此(我想)你看到的f.read()和 readlines()是前者将整个文件作为一个大的 字符串对象(即不是小对象),后者返回一个列表 每行都是python对象的行数。

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

你在尝试哪个版本的python?

我在Python2.7/Win7上做了一个测试,果然成功了,内存被释放了。

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

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,因为抛出异常会使代码变慢:

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:

Memory usage with defaultdict

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

enter image description here

相关问题 更多 >

    热门问题