Python:是否在引发MemoryError之前运行垃圾收集器?

时间:2014-03-16 17:43:14

标签: python memory numpy garbage-collection out-of-memory

在Python代码中迭代了涉及内存和CPU密集数值计算的30个问题的序列,我观察到Python过程的内存消耗在30次迭代中的每次迭代开始时增长了大约800MB,最后在第8次迭代中引发MemoryError(其中系统的内存实际上已耗尽)。但是,如果我import gc并让gc.collect()在每次迭代后运行,那么内存消耗将保持恒定在~2.5GB并且Python代码在解决所有30个问题后很好地终止。代码仅使用2个连续问题的数据,并且没有引用周期(否则手动垃圾收集也无法保持内存消耗)。

问题

如果Python在提升MemoryError之前尝试运行垃圾收集器,则此行为会引发问题。在我看来,这将是一个完全明智的事情,但也许有理由反对这个?

对此进行了类似的观察:https://stackoverflow.com/a/4319539/1219479

1 个答案:

答案 0 :(得分:4)

实际上,参考周期,这是手动gc.collect()调用能够回收内存的唯一原因。

在Python中(我在这里假设CPython),垃圾收集器的唯一目的是打破参考周期。如果没有,对象将被销毁,并且在最后一次引用它们的确切时刻回收它们的记忆。

关于何时运行垃圾收集器,完整文档位于:http://docs.python.org/2/library/gc.html

它的TLDR是Python维护对象分配和解除分配的内部计数器。每当(allocations - deallocations)达到700(阈值0)时,就会运行垃圾收集并重置两个计数器。

每次集合发生时(自动或使用gc.collect()手动运行),都会收集第0代(所有尚未在集合中存活的对象)(即没有可访问引用的对象)遍历,寻找参考周期 - 如果找到任何参考周期,周期被打破,可能导致对象被破坏,因为没有参考左)。该集合后剩余的所有对象都将移至第1代。

每10个集合(阈值1),第1代也被收集,第1代中 的所有对象都被移动到第2代。第1代的每10个集合(即每个100个集合 - 阈值2),第2代也被收集。幸存下来的对象留在第2代 - 没有第3代。

可以通过调用gc.set_threshold(threshold0, threshold1, threshold2)来设置这3个阈值。

这对您的计划意味着什么:

  1. GC不是CPython用于回收内存的机制(refcounting is)。 GC打破"死"中的参考周期对象,可能导致其中一些被摧毁。
  2. 不,无法保证GC在MemoryError被提升之前运行。
  3. 您有参考周期。试着摆脱它们。