1

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

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

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

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

4

2 回答 2

7

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

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

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

这都是根据本文档关于对 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 中可能更简单。

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

于 2013-08-02T21:05:29.877 回答
2

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

您提到您在内存中存储了很多内容,可能在字典或集合中,然后仅输出到一个文件中。

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

附加甚至可以从输入逐行完成,因此不需要存储。

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

于 2013-08-02T20:55:42.803 回答