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
像其他人所说,至少需要以下两个变化:
不要创建一个庞大的整数列表 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)不要创建一个巨大的字符串作为一次写入的完整文件正文
# 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)
这里的主要罪魁祸首是如上所述的range()调用.它将创建一个包含1500万成员的列表,这将占用200 MB的内存,并且有15个进程,即3GB.
但也不要将整个15MB文件读入data(),从响应中逐位读取.将这15MB的内容粘贴到一个变量中会比响应中的一点一点地消耗15MB的内存.
您可能只想考虑提取数据,直到您用完indata,并将您提取的数据计数与第一个字节应该是的数据进行比较.那么你既不需要range()也不需要xrange().对我来说似乎更加pythonic.:)
考虑使用xrange()而不是range(),我相信xrange是一个生成器,而range()扩展整个列表.
我要么说要么不将整个文件读入内存,要么不要将整个解压缩的结构保留在内存中.
目前你保持记忆,同时,这将是相当大的.所以你在内存中至少有两份数据副本,还有一些元数据.
也是最后一行
f.write(os.linesep.join(data))
Run Code Online (Sandbox Code Playgroud)
实际上可能意味着你暂时在内存中获得了第三个副本(一个包含整个输出文件的大字符串).
所以我会说你以非常低效的方式做这件事,将整个输入文件,整个输出文件和相当数量的中间数据保存在内存中.
使用生成器解析它是一个相当不错的主意.考虑在生成它之后写出每个记录(然后可以将其丢弃并重新使用内存),或者如果这导致过多的写请求,则将它们一次批量处理为100行.
同样,阅读响应可以以块的形式完成.因为它们是固定记录,所以这应该相当容易.