清除Python中内存密集型过程之间的内存

时间:2013-08-02 20:40:05

标签: python memory-management garbage-collection

我需要按顺序读取大文本文件,将大量数据存储在内存中,然后使用它们编写大文件。这些读/写周期一次完成一个,并且没有通用数据,因此我不需要在它们之间共享任何内存。

我尝试将这些过程放在一个脚本中,希望垃圾收集器在RAM满了时删除旧的,不再需要的对象。但事实并非如此。即使我在循环之间明确删除了对象,也需要比单独运行程序要长得多。

具体来说,使用所有可用的RAM但几乎没有CPU,进程将挂起。它在gc.collect()被调用时也挂起。因此,我决定将每个读/写过程拆分为单独的脚本,并使用execfile()从中心脚本调用它们。遗憾的是,这并没有解决任何问题。记忆仍然堆积如山。

我使用了简单明了的解决方案,即从shell脚本调用下标而不是使用execfile()。但是,我想知道是否有办法使这项工作。有什么输入吗?

2 个答案:

答案 0 :(得分:7)

立即释放任何没有引用的CPython对象。 Python会定期执行垃圾收集,以处理仅相互引用但程序无法访问的对象组(循环引用)。如果需要在特定时间(gc.collect())完成,您可以手动调用垃圾收集器来清除它们。这使得内存可供Python脚本重用,但可能会立即(或曾经)将内存释放回操作系统。

CPython在256KB竞技场中分配内存,它将其划分为4KB池,这些池进一步细分为块,这些块被指定用于特定大小的对象(这些通常是相似类型但不一定是这样)。此内存可以在Python进程中重用,但在整个竞技场为空之前,它不会被释放回操作系统。

现在,在2005年之前,一些常用的对象类型没有使用这种方案。例如,一旦创建了'int'或'float',即使它被Python释放,该内存也永远不会返回给操作系统,但它可以重用于这些类型的其他对象。 (当然小的int是共享的,不占用任何额外的内存,但如果你分配了一个大的intfloat的列表,那么即使在释放这些对象之后,CPython也会保留内存。)Python还保留了一些由列表和字典分配的内存(例如最近的80个列表)。

这完全是根据this document关于Python内存分配器大约2.3版的改进。我理解自那时以来已经做了一些进一步的工作,因此一些细节可能已经改变(int / float情况已经根据下面的arbautjc评论得到纠正)但基本情况仍然存在:性能原因是,Python不会立即将所有内存返回给操作系统,因为malloc()对小分配的开销相对较高,而内存越多,内存越慢。因此Python只有mallocs()大块的内存,并在这些块本身内部分配内存,并且只有当它们完全为空时才将这些块返回给操作系统。

您可以尝试其他Python实现,例如PyPy(旨在尽可能与CPython兼容),Jython(在JVM上运行)或IronPython(在.NET CLR上运行)以查看其内存管理是否更多与你正在做的事情相关的。如果您当前正在使用32位Python,则可以尝试使用64位Python(假设您的CPU和操作系统支持它)。

但是,从shell脚本顺序调用脚本的方法对我来说似乎完全没问题。您可以使用subprocess模块在​​Python中编写主脚本,但它在shell中可能更简单。

虽然不知道你的脚本在做什么,但很难猜出导致这种情况的原因。

答案 1 :(得分:2)

通常在这种情况下,重构是唯一的出路。

你提到你在内存中存储了很多东西,可能是在dict或set中,然后只输出到一个文件中。

也许您可以在处理完每个输入后将输出附加到输出文件,然后在处理新输入文件之前快速清理。这样,可以减少RAM的使用。

甚至可以从输入逐行完成追加,这样就不需要存储。

由于我不知道您正在使用的具体算法,因为您提到不需要文件之间的共享,这可能会有所帮助。记得也要刷新输出:P