Skip to main content
 首页 » 编程设计

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

2025年05月04日12飞鱼

我需要顺序读取一个大文本文件,在内存中存储大量数据,然后用它们写入一个大文件。这些读/写周期一次完成一个,没有公共(public)数据,所以我不需要在它们之间共享任何内存。

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

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

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

请您参考如下方法:

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

CPython 在 256KB 的区域中分配内存,并将其划分为 4KB 的池,这些池进一步分割为 block ,这些 block 指定用于特定大小的对象(这些对象通常具有相似的类型,但不必如此)。此内存可以在 Python 进程中重复使用,但它不会释放回操作系统,直到整个区域为空。

现在,在 2005 年之前,一些常用类型的对象并没有使用这种方案。例如,一旦您创建了一个“int”或“float”,即使 Python 释放了该内存,它也永远不会返回给操作系统,但它可以重新用于这些类型的其他对象。 (当然,小的 int 是共享的,不会占用任何额外的内存,但是如果你分配了一个大的 int 列表,或者 floats,即使在释放这些对象后,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 位(假设您的 CPU 和操作系统支持)。

但是,您从 shell 脚本顺序调用脚本的方法对我来说似乎非常好。您可以使用 subprocess 模块在 Python 中编写主脚本,但在 shell 中可能更简单。

但是,如果不了解您的脚本正在做什么,就很难猜测是什么导致了这种情况。