【问题标题】:Why is the destructor called when the CPython garbage collector is disabled?为什么在禁用 CPython 垃圾收集器时调用析构函数?
【发布时间】:2010-04-05 11:28:14
【问题描述】:

我试图了解 CPython 垃圾收集器的内部结构,特别是在调用析构函数时。到目前为止,这种行为是直观的,但下面的案例让我很头疼:

  1. 禁用 GC。
  2. 创建一个对象,然后删除对它的引用。
  3. 对象被销毁,_____del_____ 方法被调用。

我认为只有启用垃圾收集器才会发生这种情况。有人可以解释为什么会这样吗?有没有办法推迟调用析构函数?

import gc
import unittest

_destroyed = False

class MyClass(object):

    def __del__(self):
        global _destroyed
        _destroyed = True

class GarbageCollectionTest(unittest.TestCase):

    def testExplicitGarbageCollection(self):
        gc.disable()
        ref = MyClass()
        ref = None
        # The next test fails. 
        # The object is automatically destroyed even with the collector turned off.
        self.assertFalse(_destroyed) 
        gc.collect()
        self.assertTrue(_destroyed)

if __name__=='__main__':
    unittest.main()

免责声明:此代码不适用于生产 - 我已经注意到这是非常特定于实现的,不适用于 Jython。

【问题讨论】:

标签: python garbage-collection cpython


【解决方案1】:

Python既有引用计数垃圾回收,也有循环垃圾回收,gc模块控制的是后者。引用计数不能被禁用,因此当循环垃圾收集器关闭时仍然会发生。

由于在ref = None 之后没有对您的对象的引用,它的__del__ 方法被调用,因为它的引用计数变为零。

the documentation 中有一条线索:“自从收集器补充 Python 中已经使用的引用计数......”(我的重点)。

您可以通过使对象引用自身来停止触发第一个断言,这样它的引用计数就不会变为零,例如通过给它这个构造函数:

def __init__(self):
    self.myself = self

但如果你这样做,第二个断言就会触发。这是因为使用 __del__ 方法的垃圾循环不会被收集 - 请参阅 gc.garbage 的文档。

【讨论】:

    【解决方案2】:

    文档here(原始链接指向一个文档部分,在 Python 3.5 之前是 here,后来是 relocated)解释了所谓的“可选垃圾收集器”实际上是如何收集 循环垃圾(引用计数无法捕获的那种)(另请参阅here)。引用计数在here 中进行了解释,并强调了它与循环gc 的相互作用:

    虽然 Python 使用传统的 引用计数实现,它 还提供了一个循环检测器, 用于检测参考周期。这 让应用程序不用担心 创建直接或间接循环 参考;这些都是弱点 垃圾收集使用实现 只有引用计数。参考 循环由对象组成 包含(可能是间接的)引用 对自己,使每个对象在 循环有一个引用计数 非零。典型参考 计数实现无法 收回属于任何人的记忆 引用循环中的对象,或 从对象中引用 循环,即使没有 对循环的进一步引用 自己。

    【讨论】:

      【解决方案3】:

      根据您对垃圾收集器的定义,CPython 有两个垃圾收集器,一个是引用计数,一个是另一个。
      引用计数器始终在工作,并且无法关闭,因为它是一种非常快速且轻量级的计数器,不会显着影响系统的运行时间。
      另一个(我认为是各种标记和扫描)经常运行,并且可以禁用。这是因为它要求解释器在运行时暂停,这可能发生在错误的时刻,并且会消耗大量的 CPU 时间。
      当您希望做一些时间紧迫的事情时,这种禁用它的功能就在那里,并且缺少此 GC 不会给您带来任何问题。

      【讨论】:

      • 这个“两个垃圾收集器”的实现是否记录在某个地方?
      • 查看 Alex Martelli 的答案及其相关链接。它可能比我能想出的任何东西都要好。
      猜你喜欢
      • 1970-01-01
      • 2014-06-02
      • 1970-01-01
      • 2015-03-22
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 2011-10-15
      相关资源
      最近更新 更多