如何编写内存高效的Python程序?

jac*_*ack 9 python memory memory-management

据说Python自动管理内存.我很困惑,因为我有一个Python程序一直使用超过2GB的内存.

它是一个简单的多线程二进制数据下载器和解包器.

def GetData(url):
    req = urllib2.Request(url)
    response = urllib2.urlopen(req)
    data = response.read() // data size is about 15MB
    response.close()
    count = struct.unpack("!I", data[:4])
    for i in range(0, count):
        UNPACK FIXED LENGTH OF BINARY DATA HERE
        yield (field1, field2, field3)

class MyThread(threading.Thread):
    def __init__(self, total, daterange, tickers):
        threading.Thread.__init__(self)

    def stop(self):
        self._Thread__stop()

    def run(self):
        GET URL FOR EACH REQUEST
        data = []
        items = GetData(url)
        for item in items:
            data.append(';'.join(item))
        f = open(filename, 'w')
        f.write(os.linesep.join(data))
        f.close()
Run Code Online (Sandbox Code Playgroud)

有15个线程正在运行.每个请求获取15MB数据并将其解压缩并保存到本地文本文件中.该程序如何消耗超过2GB的内存?在这种情况下,我是否需要进行任何内存回收工作?如何查看每个对象或函数使用多少内存?

我将非常感谢您提供有关如何使python程序以内存高效模式运行的所有建议或提示.

编辑:这是"cat/proc/meminfo"的输出

MemTotal:        7975216 kB
MemFree:          732368 kB
Buffers:           38032 kB
Cached:          4365664 kB
SwapCached:        14016 kB
Active:          2182264 kB
Inactive:        4836612 kB
Run Code Online (Sandbox Code Playgroud)

tzo*_*zot 11

像其他人所说,至少需要以下两个变化:

  1. 不要创建一个庞大的整数列表 range

    # use xrange
    for i in xrange(0, count):
        # UNPACK FIXED LENGTH OF BINARY DATA HERE
        yield (field1, field2, field3)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不要创建一个巨大的字符串作为一次写入的完整文件正文

    # use writelines
    f = open(filename, 'w')
    f.writelines((datum + os.linesep) for datum in data)
    f.close()
    
    Run Code Online (Sandbox Code Playgroud)

更好的是,您可以将文件写为:

    items = GetData(url)
    f = open(filename, 'w')
    for item in items:
        f.write(';'.join(item) + os.linesep)
    f.close()
Run Code Online (Sandbox Code Playgroud)


Len*_*bro 8

这里的主要罪魁祸首是如上所述的range()调用.它将创建一个包含1500万成员的列表,这将占用200 MB的内存,并且有15个进程,即3GB.

但也不要将整个15MB文件读入data(),从响应中逐位读取.将这15MB的内容粘贴到一个变量中会比响应中的一点一点地消耗15MB的内存.

您可能只想考虑提取数据,直到您用完indata,并将您提取的数据计数与第一个字节应该是的数据进行比较.那么你既不需要range()也不需要xrange().对我来说似乎更加pythonic.:)


Mar*_*rkR 6

考虑使用xrange()而不是range(),我相信xrange是一个生成器,而range()扩展整个列表.

我要么说要么不将整个文件读入内存,要么不要将整个解压缩的结构保留在内存中.

目前你保持记忆,同时,这将是相当大的.所以你在内存中至少有两份数据副本,还有一些元数据.

也是最后一行

    f.write(os.linesep.join(data))
Run Code Online (Sandbox Code Playgroud)

实际上可能意味着你暂时在内存中获得了第三个副本(一个包含整个输出文件的大字符串).

所以我会说你以非常低效的方式做这件事,将整个输入文件,整个输出文件和相当数量的中间数据保存在内存中.

使用生成器解析它是一个相当不错的主意.考虑在生成它之后写出每个记录(然后可以将其丢弃并重新使用内存),或者如果这导致过多的写请求,则将它们一次批量处理为100行.

同样,阅读响应可以以块的形式完成.因为它们是固定记录,所以这应该相当容易.


Cal*_*ngh 5

最后一行肯定应该是f.close()?那些尾随的parens有点重要.