多进程管理器进程无法释放内存

2024-05-19 19:48:13 发布

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

在我开发的应用程序中,我使用multiprocessing.BaseManager与主进程并行执行一些繁重复杂的计算。我使用管理器而不是池,因为这些计算是作为class实现的,只需要偶尔执行一次。在

每次我在管理器中创建计算类的新实例时,调用其方法,获取结果,然后删除实例并调用gc.收集()在经理那里。在

下面是一个伪代码来演示这种情况:

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(),它的增长速度会更快。所以我想这个对象确实被删除了,垃圾回收器也在工作,但是仍然有一些东西被留下。在

下面是管理器进程在五轮计算中使用的驻留内存的图:http://i.imgur.com/BY6KuXD.png

我的问题是:

  1. 我需要在MyComputer实现中搜索内存泄漏,还是这只是python内存管理系统的一个特性?在
  2. 如果后者是真的,是否有任何方法可以强制管理器进程将其“释放”的内存返回给操作系统?在

Tags: 内存管理器进程managerargsresultsgcmany
2条回答

经过一个多星期的研究,我回答了自己的问题:

  1. 所描述的内存使用配置文件确实是Python内存管理系统的一个特性,它不会释放分配给小对象的内存。因此,如果在计算过程中生成的数据量很大,最好是预先分配将包含它的对象。NumPy数组是一个选项;也可能是内置数组。在
  2. 不,没办法那样做。更重要的是:正如我所了解到的,即使在C中,free()调用也不一定会导致内存返回到操作系统。在

调查的另一个重要结论是:

注意这些巨大的内存峰值(http://i.imgur.com/BY6KuXD.png)。它们比产生的任何结果(约250Mb)大得多。事实证明,这是因为他们是在腌制过程中未经腌制而成的。酸洗是一个非常耗时的过程;它的内存使用量与要酸洗的对象的大小呈非线性关系。因此,如果你(不)腌制一个~10Mb大的对象,它将使用~12-13Mb,但(un)酸洗~250Mb则需要800-1000Mb!因此,为了提取一个大对象(,其中包括管道、队列、连接、托架等的任何用法,),您需要以某种方式序列化该过程。在

很难猜出是什么问题。因为内存泄漏总是很难找到。 如果您没有memory_profiler,我建议您安装memory_profiler。它可以帮助你很容易地找到记忆问题。在

只是一个如何使用它的例子:

在测试.py

@profile                                                                        
def foo():                                                                      
    f = open('CC_2014.csv', 'rb')                                               
    lines_f = f.readlines()*10000                                               
    f.close()                                                                   
    lines_f = None                                                              

foo()

如您所见,我向我怀疑有内存问题的函数添加了@profile装饰器。 然后按如下方式运行脚本:

^{pr2}$

结果是:

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

从这个输出可以很容易地看出哪一行占用了大量内存。在

相关问题 更多 >