【发布时间】:2014-03-14 06:30:13
【问题描述】:
我有一个 Python 3.x 程序,它处理几个包含大量数据的大型文本文件,这些数据有时会超出我微不足道的工作站的内存限制。从一些基本的内存分析来看,似乎在使用生成器时,我的脚本的内存使用量激增以容纳连续的元素,使用的内存是我预期的两倍。
我制作了一个简单的独立示例来测试生成器,我在 Python 2.7、3.3 和 3.4 中得到了类似的结果。我的测试代码如下,memory_usage() 是this function from an SO question 的修改版本,它使用/proc/self/status 并在我观看时与top 一致。 resource 可能是一种更跨平台的方法:
import sys, resource, gc, time
def biggen():
sizes = 1, 1, 10, 1, 1, 10, 10, 1, 1, 10, 10, 20, 1, 1, 20, 20, 1, 1
for size in sizes:
data = [1] * int(size * 1e6)
#time.sleep(1)
yield data
def consumer():
for data in biggen():
rusage = resource.getrusage(resource.RUSAGE_SELF)
peak_mb = rusage.ru_maxrss/1024.0
print('Peak: {0:6.1f} MB, Data Len: {1:6.1f} M'.format(
peak_mb, len(data)/1e6))
#print(memory_usage()) #
data = None # go
del data # away
gc.collect() # please.
# def memory_usage():
# """Memory usage of the current process, requires /proc/self/status"""
# # https://stackoverflow.com/a/898406/194586
# result = {'peak': 0, 'rss': 0}
# for line in open('/proc/self/status'):
# parts = line.split()
# key = parts[0][2:-1].lower()
# if key in result:
# result[key] = int(parts[1])/1024.0
# return 'Peak: {peak:6.1f} MB, Current: {rss:6.1f} MB'.format(**result)
print(sys.version)
consumer()
在实践中,我将处理来自这种生成器循环的数据,只保存我需要的数据,然后丢弃它。
当我运行上述脚本时,两个大元素串联在一起(数据大小可能变化很大),似乎 Python 在释放前一个之前计算下一个,导致内存使用量增加一倍。
$ python genmem.py
2.7.3 (default, Sep 26 2013, 20:08:41)
[GCC 4.6.3]
Peak: 7.9 MB, Data Len: 1.0 M
Peak: 11.5 MB, Data Len: 1.0 M
Peak: 45.8 MB, Data Len: 10.0 M
Peak: 45.9 MB, Data Len: 1.0 M
Peak: 45.9 MB, Data Len: 1.0 M
Peak: 45.9 MB, Data Len: 10.0 M
# ^^ not much different versus previous 10M-list
Peak: 80.2 MB, Data Len: 10.0 M
# ^^ same list size, but new memory peak at roughly twice the usage
Peak: 80.2 MB, Data Len: 1.0 M
Peak: 80.2 MB, Data Len: 1.0 M
Peak: 80.2 MB, Data Len: 10.0 M
Peak: 80.2 MB, Data Len: 10.0 M
Peak: 118.3 MB, Data Len: 20.0 M
# ^^ and again... (20+10)*x
Peak: 118.3 MB, Data Len: 1.0 M
Peak: 118.3 MB, Data Len: 1.0 M
Peak: 118.3 MB, Data Len: 20.0 M
Peak: 156.5 MB, Data Len: 20.0 M
# ^^ and again. (20+20)*x
Peak: 156.5 MB, Data Len: 1.0 M
Peak: 156.5 MB, Data Len: 1.0 M
data = None、del data 和 gc.collect() 的疯狂传送带和吊带和管道胶带方法无济于事。
我很确定生成器本身不会在内存上加倍,因为否则它产生的单个大值会增加峰值使用率,并且在相同的迭代中出现了一个大对象;它只是大的连续对象。
如何保存我的记忆?
【问题讨论】:
-
id_ = None没用,因为 _id 被 ids 引用。 -
如果你只关心
data的第一个元素,你应该重构plate.good_data给你一个生成器,然后只获取它产生的第一个元素,不是吗?似乎您所有的问题都来自将大量data加载到内存中,其中绝大多数您并不关心。 -
如何为
i和data使用临时列表?类似:for [i, data] in enumerate(plate.good_data())。垃圾收集器有可能在这里做点什么吗? -
@roippi 正在使用中,我会将
data传递给另一个函数,该函数将其分解为更易于管理的东西。我试图逐步制作更多最小的“程序”来缩小问题的范围,即使没有保留对它的任何引用(我可以看到),它仍然会占用内存。 -
@roippi 我也许可以修改我的生成器以接受一个在返回项目之前执行上述处理的函数......