在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
答案 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个阈值。
这对您的计划意味着什么:
MemoryError
被提升之前运行。