在Python中卸载模块

Sla*_*a V 53 python memory-leaks

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
Run Code Online (Sandbox Code Playgroud)

更新 我已经与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
Run Code Online (Sandbox Code Playgroud)

我们保存一份副本,sys.modules以便稍后尝试恢复.所以,这是一个基线4074个对象.理想情况下,我们应该以某种方式回归.

我们导入一个模块:

import httplib
print len(gc.get_objects()) # 7063 objects in memory
Run Code Online (Sandbox Code Playgroud)

我们有7K非垃圾对象.我们试着httplib从中删除sys.modules.

sys.modules.pop('httplib')
gc.collect()
print len(gc.get_objects()) # 7063 objects in memory
Run Code Online (Sandbox Code Playgroud)

好吧,那没用.嗯,但是没有参考__main__?哦耶:

del httplib
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory
Run Code Online (Sandbox Code Playgroud)

万岁,300件物品.不过,没有雪茄,超过4000个原始物品.我们试着sys.modules从副本中恢复.

sys.modules = sm
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory
Run Code Online (Sandbox Code Playgroud)

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

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

当地人?

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

什么..如果我们imported内部的模块exec怎么办?

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

现在,这不公平,它导入了__main__,为什么?它应该永远不会离开local_dict......唉!我们回到完全进口httplib.也许如果我们用虚拟对象替换它?

from types import ModuleType
import sys
print len(gc.get_objects())  # 7064 objects in memory
Run Code Online (Sandbox Code Playgroud)

血腥.....!!

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

模具模块,模具!!

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

好吧,经过所有的尝试,最好的是从起点开始的+2675(差不多+ 50%)...这只是来自一个模块......那里面甚至都没有什么大的......

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

完整的来源,更简单的复制形式:http://gist.github.com/450606

Dan*_*ach 16

Python不支持卸载模块.

但是,除非您的程序随着时间的推移加载无限数量的模块,否则这不是内存泄漏的来源.模块通常在启动时加载一次,就是这样.你的记忆泄漏很可能在其他地方.

在不太可能的情况下,您的程序确实会随着时间的推移加载无限数量的模块,您应该重新设计您的程序.;-)

  • @Glenn:它们是垃圾收集对象,是的.真假也是如此.你能把参考计数降到0吗?不那么容易.另见:http://bit.ly/9mvndb (4认同)
  • 是的,它确实加载了合理无限数量的模块——它是一个 Web 应用程序服务器,它接受自己源代码的新修订版并重新加载它(这是非常标准的 Web 任务)。泄漏是因为旧代码仍然存在于内存中,即使被替换,即使无法访问...... (2认同)
  • @Slava:您可能想查看 `mod_python` 的源代码,它有自己的导入器,旨在处理重新加载模块而不会产生内存泄漏。那里可能有一些您可以使用的代码。 (2认同)
  • @Slava(3评论):部分,但不完全.其中也有很多Python代码,包括导入器.请参阅[来源](http://svn.apache.org/viewvc/quetzalcoatl/mod_python/trunk/lib/python/mod_python/importer.py?view=markup). (2认同)

mod*_*itt 11

我在 python3(10 年后)(现在)中找不到对此的权威观点python3.8。然而,我们现在可以在百分比方面做得更好。

import gc
import sys

the_objs = gc.get_objects()
print(len(gc.get_objects())) # 5754 objects in memory
origin_modules = set(sys.modules.keys())
import http.client # it was renamed ;)

print(len(gc.get_objects())) # 9564 objects in memory
for new_mod in set(sys.modules.keys()) - origin_modules:
    del sys.modules[new_mod]
    try:
        del globals()[new_mod]
    except KeyError:
        pass
    try:
        del locals()[new_mod]
    except KeyError:
        pass
del origin_modules
# importlib.invalidate_caches()  happens to not do anything
gc.collect()
print(len(gc.get_objects())) # 6528 objects in memory 
Run Code Online (Sandbox Code Playgroud)

仅增长13%。如果您查看 new 中加载的对象类型gc.get_objects,其中一些是内置函数、源代码、random.*实用程序、datetime实用程序等。我主要将其保留在这里作为更新以启动 @shuttle 的对话,并将删除如果我们可以取得更大的进步。

  • 感谢这里的努力!看到百分比变化非常有趣 (2认同)

Blu*_*eft 6

我不知道Python,但在其他语言中,调用相当于gc.collect()确实释放未使用的内存-如果/当实际所需的内存也只会释放内存。

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


ili*_*dis 5

Python's small object manager rarely returns memory back to the Operating System.这里这里。因此,严格来说,即使对象被“gc 收集”,python 也有(按设计)一种内存泄漏。