强制Python释放对象以释放内存



我正在运行以下代码:

from myUtilities import myObject
for year in range(2006,2015):
    front = 'D:\newFilings\'
    back = '\*\dirTYPE\*.sgml'
    path = front + str(year) + back
    sgmlFilings = glob.glob(path)
    for each in sgmlFilings:
        header = myObject(each)
        try:
            tagged = header.process_tagged('G:')
        except Exception as e:
            outref = open('D:\ProblemFiles.txt','a')
            outref.write(each 'n')
            outref.close()
            print each

如果我从重新启动开始,python的内存分配/消耗相当小。但随着时间的推移,它会显著增加,最终在大约一天后,我的可用内存非常少(安装了24GB[缓存了294 mb空闲23960]),Python在Windows任务管理器列表中声称的内存为3GB。在对文件集合运行代码所需的三天时间里,我看到了这种增长。

我的印象是,既然我在做每件事

tagged = header.process_tagged('G:')

与每个循环相关联的内存将被释放并进行垃圾收集。

我能做些什么来强迫释放这段记忆吗。虽然我还没有运行统计数据,但我可以通过观察磁盘上的活动来判断,随着时间的推移,这个过程会变慢(内存块越来越大)。

编辑

我看了下面提到的问题,我认为这些问题与我在另一个问题中理解的问题不同,因为它们抓住了对象(三角形列表),需要整个列表进行计算。在每个循环中,我都会读取一个文件,对该文件进行一些处理,然后将其写回磁盘。然后我正在阅读下一个文件。

关于可能的内存泄漏,我在myObject 中使用LXML

注意,自从这个问题的第一次迭代以来,我添加了MyUtilities导入myObject的行。MyUtilities拥有完成所有的代码

关于发布我的myUtilities代码——这偏离了基本问题——我已经完成了标题和标记,每次标记的迭代都会做一些事情,并将结果写入另一个驱动器,事实上是一个新格式化的驱动器。

我曾考虑过使用多处理,但我没有这样做,因为我有一个模糊的想法,即由于这是I/O密集型的,我将竞争驱动器头——也许这是错误的,但由于每次迭代都需要我写几百MB的文件,我认为我将竞争写甚至读时间。

UPDATE-所以我在myObjectclass中有一个案例,其中使用打开了一个文件

myString = open(somefile).read()

我把它改成

with open(somefile,'r') as fHandle:

`    myString = fHandle.read()`

(很抱歉格式化-仍在挣扎)

然而,这并没有明显的影响。当我开始一个新的周期时,我有4000 mb的缓存内存,经过22分钟和27K文件的处理,我有大约26000 mb的缓存存储器。

我很感激下面的所有答案和评论,并且一整天都在阅读和测试各种东西。我会更新这个,因为我认为这项任务需要一周的时间,现在看起来可能需要一个多月的时间。

我不断收到关于代码其余部分的问题。然而,它超过了800行,对我来说,这有点偏离了的核心问题

因此创建了myObject的实例然后我们将myObject中包含的方法应用于标头

这基本上就是文件转换。读取一个文件,并制作文件各部分的副本并将其写入磁盘。

对我来说,中心问题是标头标记显然存在一些持久性。在开始下一个周期之前,我如何处理与标题或标记相关的所有内容。

在过去的14个小时左右,我一直在运行代码。当它经过第一个周期时,处理27K个文件大约需要22分钟,现在处理大约相同的数量需要一个半小时。

仅仅运行gc.collect是不起作用的。我停止了程序,在解释器中尝试了一下,但我没有看到内存统计数据的变化。

EDIT在阅读了下面的内存分配器描述后,我认为缓存中的占用量不是问题所在,而是运行的python进程占用的量。所以新的测试是从命令行运行代码。我将继续观察和监控,一旦我看到发生了什么,我会发布更多的帖子。

EDIT:仍然在挣扎,但已经设置了从一个bat文件运行的代码,该文件包含来自sgmlFilings的一个循环的数据(见上文),批处理文件看起来像这个

python batch.py
python batch.py
 .
 .
 .

batch.py从读取一个队列文件开始,该文件包含要glob的目录列表,它从列表中删除第一个目录,更新列表并保存它,然后运行headertagged进程。虽然很笨拙,但由于每次迭代后python.exe都会关闭,python从不积累内存,因此进程以一致的速度运行。

原因是CPython的内存管理。Python管理内存的方式使长时间运行的程序变得困难。当您使用del语句显式释放对象时,CPython必然不会将分配的内存返回给操作系统。它保留了内存以备将来使用。解决这个问题的一种方法是使用多处理器模块,在完成作业后终止进程并创建另一个进程。通过这种方式,您可以强制释放内存,操作系统必须释放该子进程使用的内存。我也遇到过同样的问题。内存使用量随着时间的推移而过度增加,以至于系统变得不稳定和无响应。我对信号和psutil使用了不同的技术来解决它。例如,当您有一个循环并且需要在堆栈上分配和解除分配数据时,通常会出现此问题。

您可以在这里阅读更多关于Python内存分配器的信息:http://www.evanjones.ca/memoryallocator/

此工具对评测内存使用情况也很有帮助:https://pypi.python.org/pypi/memory_profiler

还有一件事,将插槽添加到myObject中,您的对象中似乎有固定的插槽,这也有助于减少ram的使用。没有指定插槽的对象会分配更多的ram来处理您稍后可能添加到其中的动态属性:http://tech.oyster.com/save-ram-with-python-slots/

您可以使用gc模块强制进行垃圾收集。特别是gc.collect()函数。

然而,这可能无法解决您的问题,因为gc可能正在运行,但您使用的库/代码包含内存泄漏,或者库/代码在某个地方保留了一些引用。无论如何,我怀疑gc是否是这里的问题。

有时,您可能有一些代码可以保持对所需对象的有效引用。在这种情况下,您可以考虑在不再需要它们时显式地del对它们进行处理,但事实并非如此。


还要记住,python进程的内存使用量实际上可能比操作系统报告的要小得多。特别是对free()的调用不需要将内存返回给操作系统(通常在执行小分配时不会发生这种情况),因此您看到的可能是迄今为止内存使用率的最高峰值,而不是当前使用率。此外,Python在C的内存分配层之上使用了另一层内存分配,这使得很难对内存使用情况进行分析。然而,它的记忆不断上升,这可能不是事实。

您应该使用类似Guppy的东西来评测内存使用情况。

使用gc模块可以对这些东西进行一定程度的控制。具体来说,您可以尝试合并

gc.collect() 

在你的环的身体。

在诉诸强制垃圾收集之前(从来都不是一个好主意);尝试一些基本的东西:

  1. 使用glob.iglob(生成器),而不是一次获取所有文件的列表。

  2. myObject(each)方法中,确保关闭文件或使用with statement使其自动关闭;否则,它将留在记忆中吞噬空间。

  3. 不要打开和关闭文件;只需打开文件一次即可写入异常处理程序。

由于您还没有发布进行处理的实际代码(因此,可能是内存问题的原因),因此很难推荐具体细节。

最新更新