Python 模块是否会被垃圾回收?

Nin*_*non 3 python garbage-collection python-import python-3.x

如果我在 Python 中加载一个模块,它会被垃圾收集吗?解决这个问题的另一种方式是,Python 在哪里保存对 Python 模块的引用?我假设如果不再有任何引用,垃圾收集器将删除一个模块。

这是我在 Python 解释器中尝试过的一个示例:

>>> from importlib import import_module
>>> import sys
>>> import gc

>>> x = import_module('math')
>>> 'math' in sys.modules
Run Code Online (Sandbox Code Playgroud)

这输出:

真的

因此,让我们删除脚本中对该模块的引用。

>>> del x
>>> gc.collect()
>>> 'math' in sys.modules
Run Code Online (Sandbox Code Playgroud)

Python 仍然跟踪 math 模块,因为输出仍然是:

真的

但现在如果我从 中删除数学sys.modules,我将不再知道任何进一步的参考:

>>> del sys.modules['math']
>>> gc.collect()
Run Code Online (Sandbox Code Playgroud)

然而, 的输出gc.collect()是:

0

没有垃圾收集,因此该模块不再位于sys.modules我的脚本中。为什么没有被垃圾收集?

aba*_*ert 5

一般来说,至少在 3.4 及更高版本中,模块对象\xe2\x80\x99 在这方面不应该有任何特殊之处。当然,通常有\xe2\x80\x99s对每个加载模块的引用sys.modules,但如果你\xe2\x80\x99 显式删除了它,则模块应该能够消失。

\n\n

话虽这么说,过去肯定存在一些问题,在某些情况下阻止了这种情况的发生,并且我不会保证从 3.7 开始就不存在任何此类问题。

\n\n

不幸的是,您的测试实际上并没有测试任何东西。大概您\xe2\x80\x99正在使用CPython。在 CPython 中,垃圾收集器使用引用计数\xe2\x80\x94,它直接在每个对象上存储一个计数,每次将新名称绑定到它时递增和递减计数,如果计数变为 0,则立即删除它。该gc模块中有一个循环收集器,需要它来处理一些特殊情况,其中两个(或多个)对象相互引用,但没有其他人引用它们。如果该模块不是此类循环的一部分,则它会在调用之前被删除gc.collect(),因此当然会返回 0。但是 0 不会告诉您任何信息。

\n\n

您的测试还存在其他问题。

\n\n

首先,您不应该在交互式解释器中测试垃圾。各种额外的东西都以难以解释的方式保存在那里。编写测试脚本\xe2\x80\x99s要好得多。

\n\n

其次,您不应该将\xe2\x80\x99 用作math测试。它\xe2\x80\x99是一个扩展模块(即用C而不是Python编写的),即使在3.5中进行了重大更改,它们仍然不\xe2\x80\x99工作相同。它也是一个核心模块,可能是启动的一部分或解释器其他部分所需的,即使您没有从代码中引用它。因此,最好使用其他东西。

\n\n

无论如何,我认为可能有一种方法可以直接测试它,而不使用调试器,但不承诺它\xe2\x80\x99是否会工作。

\n\n

首先,您需要创建 的子类types.ModuleType,它有一个__del__打印出一些消息的方法。然后,您只需导入一个模块(.py 模块,而不是扩展模块)并将其设置__class__为该子类。这可能就像__class__ = MyModuleSubclass.py 文件中一样简单。现在,当它被收集时,它的析构函数将运行,并且您\xe2\x80\x99将有证据表明它已被收集。(好吧,证明它已被收集,除非析构函数恢复它,但如果你的析构函数除了打印静态字符串之外不执行任何操作,那么希望 \xe2\x80\x99 不用担心。)

\n