Python模块是否被垃圾收集?

时间:2018-03-07 10:52:29

标签: python python-3.x garbage-collection python-import

如果我在Python中加载模块,它是否会被垃圾收集?构建这个问题的另一种方法是,Python在哪里保留对Python模块的引用?我假设如果没有任何引用,垃圾收集器将删除一个模块。

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

T

输出:

  

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

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

>>> x = import_module('math')
>>> 'math' in sys.modules

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

  

但是现在如果我从>>> del x >>> gc.collect() >>> 'math' in sys.modules 删除数学,我不再知道任何进一步的参考:

sys.modules

然而,>>> del sys.modules['math'] >>> gc.collect() 的输出是:

  

0

没有收集任何垃圾,因此该模块不再位于gc.collect()或我的脚本中。为什么不收集垃圾?

2 个答案:

答案 0 :(得分:1)

通常,至少在3.4及更高版本中,模块对象在这方面不应该是特殊的。当然,通常会在sys.modules中引用每个已加载的模块,但如果您已明确删除它,则模块应该能够消失。

话虽如此,过去肯定存在一些问题,以防止在某些情况下发生这种情况,我不会保证3.7中没有任何此类问题。

不幸的是,您的测试实际上并未测试任何内容。大概你正在使用CPython。在CPython中,垃圾收集器使用引用计数 - 它直接在每个对象上存储一个计数,每次绑定一个新名称时递增和递减计数,如果计数变为0则立即删除它。{{{ 1}}模块是一个循环收集器,它需要处理一些特殊情况,其中两个(或更多)对象相互引用但没有其他人引用它们。如果模块不是这样一个循环的一部分,它将在你调用gc之前被删除,所以当然会返回0.但是那个0什么也没告诉你。

您的测试还有其他问题。

首先,您不应该在交互式解释器中测试垃圾。所有类型的额外东西都被保存在那里,其解释方式很复杂。编写测试脚本要好得多。

其次,您不应该使用gc.collect()作为测试。它是一个扩展模块(即用C语言而不是Python语言编写),即使在3.5中发生重大变化之后,它们仍然无法正常工作。它也是一个核心模块,可能是启动的一部分,也可能是解释器其他部分所需要的,即使您没有从代码中引用它。所以,使用其他东西要好得多。

无论如何,我认为可能有一种方法可以直接测试它,而不使用调试器,但没有任何关于它是否可行的承诺。

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

答案 1 :(得分:1)

根据abarnert的回答,我创建了以下自运行示例,演示了我试图理解的行为:

debugPrint

按原样运行输出:

  

我被删除了

     

精加工

from types import ModuleType from importlib import import_module import sys class MyModule(ModuleType): def __del__(self): print('I am being deleted') if __name__ == '__main__': x = import_module('urllib3') x.__class__ = MyModule del x del sys.modules['urllib3'] # Comment this out and urllib3 will NOT be garbage collected before the script finishes print('finishing') 行的输出已注释掉:

  

精加工

     

我被删除了

很明显,当所有对它们的引用都被删除时,模块是垃圾收集的,除非有问题的模块有些特殊,否则当应用程序和del sys.modules['urllib3']中的引用有已被删除。