用Python卸载一个模块

TL / DR:

import gc, sys print len(gc.get_objects()) # 4073 objects in memory # Attempt to unload the module import httplib del sys.modules["httplib"] httplib = None gc.collect() print len(gc.get_objects()) # 6745 objects in memory 

更新我已经联系了Python开发者关于这个问题,并且实际上在“未来五年”中将不可能完全卸载一个模块 。 (请参阅链接)

请接受,Python确实不支持在2.x中卸载严重的,基本的,难以克服的技术问题的模块。


在我最近在我的应用程序中寻找memleak的时候,我把它缩小到了模块,也就是我无法收集一个卸载的模块。 使用下面列出的任何方法卸载模块会在内存中留下数千个对象。 换句话说 – 我不能在Python中卸载一个模块

问题的其余部分是试图以某种方式垃圾收集模块。

咱们试试吧:

 import gc import sys sm = sys.modules.copy() # httplib, which we'll try to unload isn't yet # in sys.modules, so, this isn't the source of problem print len(gc.get_objects()) # 4074 objects in memory 

让我们保存一个sys.modules的副本,以便稍后恢复它。 所以,这是一个基准4074个对象。 理想的情况下,我们应该回到这个地方。

我们导入一个模块:

 import httplib print len(gc.get_objects()) # 7063 objects in memory 

我们高达7K的非垃圾对象。 让我们尝试从sys.modules删除httplib

 sys.modules.pop('httplib') gc.collect() print len(gc.get_objects()) # 7063 objects in memory 

那么,这是行不通的。 嗯,但在__main__没有参考? 哦耶:

 del httplib gc.collect() print len(gc.get_objects()) # 6746 objects in memory 

万岁,下降300个对象。 尽pipe如此,没有雪茄,这是超过4000个原始对象。 让我们尝试从复制恢复sys.modules

 sys.modules = sm gc.collect() print len(gc.get_objects()) # 6746 objects in memory 

嗯,这是毫无意义的,没有变化..也许如果我们消灭全局…

 globals().clear() import gc # we need this since gc was in globals() too gc.collect() print len(gc.get_objects()) # 6746 objects in memory 

当地人?

 locals().clear() import gc # we need this since gc was in globals() too gc.collect() print len(gc.get_objects()) # 6746 objects in memory 

什么..如果我们在exec imported一个模块呢?

 local_dict = {} exec 'import httplib' in local_dict del local_dict gc.collect() print len(gc.get_objects()) # back to 7063 objects in memory 

现在,这不公平,它将其导入__main__ ,为什么? 它应该从来没有离开local_dict …呃! 我们回到完全导入的httplib 。 也许如果我们用一个虚拟对象replace它?

 from types import ModuleType import sys print len(gc.get_objects()) # 7064 objects in memory 

血腥…..!!

 sys.modules['httplib'] = ModuleType('httplib') print len(gc.get_objects()) # 7066 objects in memory 

模组模块,死!

 import httplib for attr in dir(httplib): setattr(httplib, attr, None) gc.collect() print len(gc.get_objects()) # 6749 objects in memory 

好吧,毕竟尝试,最好的是+2675(近+ 50%)从起点…这只是一个模块…这甚至没有什么大的内部…

好的,现在认真,我的错误在哪里? 如何卸载一个模块并清除所有内容? 还是Python的模块是一个巨大的内存泄漏?

完整源代码可以更简单的复制表单: http : //gist.github.com/450606

Python不支持卸载模块。

但是,除非您的程序随着时间的推移加载了不限数量的模块,否则这不是内存泄漏的根源。 模块通常在启动时加载一次,就是这样。 你的内存泄漏很可能在别处。

在不太可能的情况下,你的程序确实会随着时间的推移加载无限数量的模块,你应该重新devise你的程序。 😉

我不确定Python,但是在其他语言中,调用相当于gc.collect()函数不会释放未使用的内存 – 只有在实际需要内存时才会释放该内存。

否则,Python将模块暂时保存在内存中是有意义的,以防需要再次加载。

(你应该尝试写出更简洁的问题;我只读了开头,其余部分已经被删除了。)我从一开始就看到一个简单的问题:

 sm = sys.modules.copy() 

你做了一个sys.modules的副本,所以现在你的副本有一个对模块的引用 – 所以当然不会被收集。 你可以用gc.get_referrers看到它的含义。

这工作正常:

 # module1.py class test(object): def __del__(self): print "unloaded module1" a = test() print "loaded module1" 

 # testing.py def run(): print "importing module1" import module1 print "finished importing module1" def main(): run() import sys del sys.modules["module1"] print "finished" if __name__ == '__main__': main() 

当我们从sys.modules中移除module1时,module1会立即被卸载,因为没有剩余的模块引用。 (在导入之后做module1 = None也是可以的 – 为了清楚起见,我只是把导入放到了另外一个函数中,所有你需要做的就是放弃你的引用。

现在,在实践中这样做有点棘手,因为有两个问题:

  • 为了收集模块,对模块的所有引用必须是不可访问的(就象收集任何对象一样)。 这意味着任何其他导入它的模块也需要被解除引用和重载。
  • 如果你从sys.modules中删除了一个模块,那么你已经创build了一个不寻常的情况:模块仍然被加载并被代码使用,但是模块加载器不再了解它。 下一次导入模块时,您将不会获得对现有模块的引用(因为您已删除了该模块的logging),所以它将加载第二个模块的共存副本。 这可能导致严重的一致性问题。 因此,在最后从sys.modules中移除它之前,请确保没有剩余的模块引用。

有一些棘手的问题通常使用它:检测哪些模块依赖于你卸载的模块; 知道是否可以卸载(取决于你的用例); 处理线程,同时检查所有这一切(看imp.acquire_lock),等等。

我可以设法做一个这样做可能是有用的情况下,但大多数时候我build议只是重新启动应用程序,如果其代码更改。 你可能会让自己头痛。