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

2024-05-04 05:12:01 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

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

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

该输出:

True

所以让我们删除脚本中对模块的引用。在

^{pr2}$

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

True

但是现在,如果我从sys.modules中删除数学,我就不再知道有任何进一步的引用:

>>> del sys.modules['math']
>>> gc.collect()

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

0

没有垃圾回收,因此模块不再在sys.modules或我的脚本中。为什么没有垃圾收集?在


Tags: 模块方法import脚本modulestrue示例sys
2条回答

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

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')

按原样运行时的输出:

I am being deleted

finishing

del sys.modules['urllib3']行注释掉的输出:

finishing

I am being deleted

很明显,当所有对模块的引用都被删除时,模块会被垃圾回收,除非所讨论的模块有点特殊,否则当应用程序和sys.modules中的引用被删除时,就会发生这种情况。在

一般来说,至少在3.4和更高版本中,模块对象在这方面不应该有任何特殊之处。当然,通常情况下,sys.modules中的每个加载模块都有一个引用,但是如果您显式删除了它,那么一个模块应该能够消失。在

尽管如此,过去肯定有一些问题阻止了这种情况的发生,我也不保证在3.7版之后不会有这样的问题。在

不幸的是,你的测试实际上并没有测试任何东西。大概你在用CPython。在CPython中,垃圾回收器使用引用计数,它直接在每个对象上存储一个计数,每次绑定新名称时递增或递减计数,如果计数为0,则立即将其删除。{orelse>在某些情况下,{2>引用两个特殊的对象,其中每个对象都引用cd2}。如果模块不是这样一个循环的一部分,它将在您调用gc.collect()之前被删除,因此它当然会返回0。但0告诉你什么也没有。在

你的测试还有其他问题。在

首先,您不应该在交互式解释器中测试垃圾。各种额外的东西都被放在那里,以一种很难解释的方式。写一个测试脚本要好得多。在

其次,您不应该使用math作为测试。它是一个扩展模块(也就是说,是用C而不是Python编写的),即使在3.5中进行了重大更改之后,它们仍然不能正常工作。它也是一个核心模块,可能是启动的一部分,也可能是解释器的其他部分所需要的,即使您没有从代码中引用它。所以,最好用别的东西。在

不管怎样,我认为有一种方法可以直接测试它,而不使用调试器,但不能保证它是否有效。在

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

相关问题 更多 >