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

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

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

问题

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

此处进行了与上述类似的观察:https://stackoverflow.com/a/4319539/1219479

最佳答案

实际上,存在 引用循环,这是手动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 代中存活的对象 that 移动到第 2 代。第 1 代的每 10 个收集(即每100 个集合——阈值 2),第 2 代也被收集。留在第 2 代中的幸存对象 - 没有第 3 代。

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

这对您的程序意味着什么:

  1. GC 不是 CPython 用来回收内存的机制(refcounting 是)。 GC 会破坏“死”对象中的引用循环,这可能会导致其中一些对象被销毁。
  2. 不,不能保证 GC 会在引发 MemoryError 之前运行。
  3. 你有引用周期。尝试摆脱它们。

关于Python:垃圾收集器是否在引发 MemoryError 之前运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22440421/

相关文章:

python - 使用 3 个索引对 4 维数组进行切片时会发生什么?

python - 将图像转换为 one-hot 数组并返回黑色图像

python - 使用groupby仅对其中一列pandas python具有相同值的行进行操作

python - 使用 python-gnupg 获取 "OSError: Unable to run gpg () - it may not be available."错误

python - 基于区域的图像检测与状态验证

python - 内存泄漏(?)Python 3.2

c - 使用指针标记字符串

python - 可以用装饰器劫持类定义吗?

android - 如何理解Android内存管理中的分页和内存映射

python - 从 numpy 压缩距离矩阵获取整行距离