Python中的对象何时进行垃圾回收?

19

在Python中,对象何时被垃圾回收?内存何时释放?垃圾回收是否影响性能?是否可以选择退出或调整GC算法?如果可以,如何操作?

3个回答

18

Python中对象何时被垃圾回收?

在CPython的源代码中有很多细节:http://svn.python.org/view/python/trunk/Modules/gcmodule.c?revision=81029&view=markup

任何时候当一个对象的引用计数降为零,该对象会立即被移除。

293 /* Python的循环垃圾回收不应该看到一个计数为0的引用:

294 * 如果某个东西已经减少到0,那么它应该在那个时候就被

295 * 释放了。

当新对象的数量大于现有对象数量的25%时,将触发完整的垃圾回收。

87 除了各种可配置的阈值之外,我们仅在以下情况下触发全面的垃圾回收:

88 如果比率

89 long_lived_pending / long_lived_total

90 高于给定值(硬编码为25%)。

内存何时被释放?

我只能找到这个信息。

781 /* 清除所有的自由列表

782 * 在收集最高一代的过程中清除所有的自由列表。

783 * 自由列表中分配的项目可能会占用一个pymalloc地区。

784 * 清除自由列表可以更早地将内存返还给操作系统。

785 */

根据这个情况,即使您将对象的引用计数降至零,Python可能仍会将其保留在自由列表中进行回收利用。我无法明确找到何时进行免费调用以将内存返还给操作系统,但我想当进行垃圾回收时,如果对象未保留在自由列表中,则会执行此操作。 垃圾回收是否会影响性能? 我所了解的任何非平凡垃圾收集器都需要CPU和内存才能运行。因此,是的,它总是会对性能产生影响。您需要进行实验并了解您的垃圾收集器。
我遇到过需要实时响应的程序问题,因为垃圾收集器不授予我控制它们何时运行或以何种时间长短运行的权限。有些奇特的情况也会导致过多的内存使用,例如Python保留空闲列表的技巧。

链接已过期。 - Marlon Abeykoon
1
GitHub的永久链接已更新 https://github.com/python/cpython/blob/0332e569c12d3dc97171546c6dc10e42c27de34b/Modules/gcmodule.c - leopold.talirz

16

这里是摘自语言参考的一部分:

对象永远不会被显式销毁;但是,当它们变得无法访问时,它们可能会被垃圾收集。 一个实现允许延迟垃圾收集或完全省略垃圾收集 - 垃圾收集的实现方式是实现质量的问题,只要不收集仍然可访问的对象即可。

CPython 实现细节: CPython 目前使用引用计数方案和(可选的)延迟检测循环链接垃圾回收,尽早收集大多数不可访问的对象,但不能保证收集包含循环引用的垃圾。有关控制循环垃圾收集的信息,请参阅gc模块的文档。其他实现方式行为不同,而CPython可能会发生变化。当对象变得无法访问时,不要依赖其立即完成最终处理(例如:始终关闭文件)。

编辑: 关于延迟垃圾回收... gc 模块允许您与垃圾回收器交互,并在需要时禁用它,更改收集频率等。但我自己没有使用过。此外,任何包含__del__方法的对象的循环不会被收集


不错,虽然有点模糊。你知道如何触发或延迟垃圾回收吗? - Matt Alcock
这是一个不同的问题,Matt Alcock已经提供了答案。 - erisco
@MattAlcock:发出还是延迟垃圾回收?(1)那是一个单独的问题。并且。(2)为什么你想要这样做?如果你不想让一个对象被垃圾回收,就把它赋值给一个变量。 - S.Lott
1
通常,高性能系统希望发出垃圾收集以确保一致的性能。想象一下,如果一个飞行控制系统在执行垃圾回收时超时了一段时间会是多么糟糕? - Matt Alcock
1
@MattAlcock 在帖子中添加了关于 gc 模块的详细信息。 - Praveen Gollakota
3
@MattAlcock说:通常高性能系统不使用任何形式的动态内存分配。在我制作雷达和声纳时,数据结构严格是静态分配的。在高性能系统中使用任何动态内存分配的想法似乎是矛盾的。在Python中关闭垃圾回收通常是通过使用C编写性能关键代码并从Python调用它来实现的。 - S.Lott

1

为了更详细地解释之前的答案并提供更多可操作的信息:

您可以使用 gc.set_threshold(threshold0[, threshold1[, threshold2]]) 来调整自动垃圾回收启动的时间:

GC将对象根据它们经历过多少次垃圾回收分类为三代。新对象被放置在最年轻的一代(第0代)。如果一个对象经过一次回收后仍存活,它就会被移动到下一代。由于第2代是最老的一代,所以在回收后该代中的对象仍然留在那里。为了决定何时运行,收集器跟踪自上次回收以来的对象分配和取消分配数量。当分配数量减去取消分配数量超过threshold0时,就开始进行回收。最初只检查第0代。如果第0代自检查以来已经检查了超过threshold1次,那么也会检查第1代。对于第三代,情况略微复杂,请参见收集最老一代以获取更多信息。

虽然我在文档中找不到默认阈值,但是通过实现的查看,阈值的默认值似乎是(CPython 3.9.1):

  • threshold0: 700
  • threshold1: 10
  • threshold2: 10

也就是说,默认情况下,自动垃圾回收应该在分配数减去释放数超过700时启动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接