在我正在开发的应用程序中,我使用multiprocessing.BaseManager
与主进程并行执行一些繁重而复杂的计算。我使用的是Manager而不是Pool,因为这些计算是以class
实现的,需要偶尔执行一次。
每次我在管理器中创建一个新的计算类实例时,调用它的'方法,取回结果,然后删除实例并在管理器中调用gc.collect()。
这是一个伪代码来演示这种情况:
import gc
from multiprocessing.managers import BaseManager
class MyComputer(object):
def compute(self, args):
#several steps of computations
return huge_list
class MyManager(BaseManager): pass
MyManager.register('MyComputer', MyComputer)
MyManager.register('gc_collect', gc.collect)
if __name__ == '__main__':
manager = MyManager()
manager.start()
#obtain args_list from the configuration file
many_results = []
for args in args_list:
comp = manager.MyComputer()
many_results.append(comp.compute(args))
del comp
manager.gc_collect()
#do somthing with many_results
计算结果很大(200Mb-600Mb)。问题是:根据top
,经理过程使用的驻留内存在计算后显着增长(50Mb到1Gb)。如果在所有计算中使用单个comp
对象或未调用manager.gc_collect()
,则快得多。所以我猜这个对象确实被删除了,垃圾收集器也能正常工作,但还是留下了一些东西。
以下是Manager过程在五轮计算中使用的驻留内存图:http://i.imgur.com/BY6KuXD.png
我的问题是:
答案 0 :(得分:1)
经过一周多的研究,我正在回答自己的问题:
调查的另一个重要结论是:
请注意这些巨大的内存峰值(http://i.imgur.com/BY6KuXD.png)。它们比产生的任何结果(~250Mb)大得多。事实证明,这是因为他们在这个过程中被腌渍了。酸洗是一个非常昂贵的过程;它的内存使用量与要被pickle的对象的大小有非线性关系。因此,如果你(un)腌制一个大约10Mb的物体,它使用~12-13Mb,但是(〜)酸洗~250Mb使用800-1000Mb!因此,为了挑选一个大对象(包括任何管道,队列,连接,架子等的使用),你需要以某种方式序列化这个过程。 / p>
答案 1 :(得分:0)
很难猜出是什么问题。因为内存泄漏总是很难找到。 如果您没有,我建议您安装memory_profiler。它可以帮助您轻松找到内存问题。
仅举例说明如何使用它:
@profile
def foo():
f = open('CC_2014.csv', 'rb')
lines_f = f.readlines()*10000
f.close()
lines_f = None
foo()
正如您所看到的,我将@profile
装饰器添加到我怀疑存在内存问题的函数中。
然后像这样运行你的脚本:
python -m memory_profiler test.py
结果是:
Line # Mem usage Increment Line Contents
================================================
1 9.316 MiB 0.000 MiB @profile
2 def foo():
3 9.316 MiB 0.000 MiB f = open('CC_2014.csv', 'rb')
4 185.215 MiB 175.898 MiB lines_f = f.readlines()*10000
5 185.211 MiB -0.004 MiB f.close()
6 9.656 MiB -175.555 MiB lines_f = None
从这个输出中你可以很容易地看到哪一行占用了大量的内存。