Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
卸载Python中的模块_Python_Memory Leaks - Fatal编程技术网

卸载Python中的模块

卸载Python中的模块,python,memory-leaks,Python,Memory Leaks,TL/DR: 更新 我已经就这个问题联系了Python开发人员,事实上,这完全是“在未来五年”。(见链接) 请接受Python确实不支持在2.x中卸载严重、基本、无法克服的技术问题的模块 在我最近在我的应用程序中寻找memleak时,我把它缩小到了模块,也就是说我无法垃圾收集一个卸载的模块。使用下面列出的任何方法卸载模块会在内存中留下数千个对象。换句话说,我无法卸载Python中的模块 问题的其余部分是试图以某种方式对模块进行垃圾收集 让我们试试: import gc, sys print

TL/DR:


更新 我已经就这个问题联系了Python开发人员,事实上,这完全是“在未来五年”。(见链接)

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


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

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

让我们试试:

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
让我们保存一份
sys.modules
的副本,以便稍后尝试还原它。 这是一个基准4074个对象。理想情况下,我们应该以某种方式回到这一点

让我们导入一个模块:

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
我们有多达7K个非垃圾对象。 让我们尝试从
sys.modules
中删除
httplib

import httplib
print len(gc.get_objects()) # 7063 objects in memory
嗯,那没用。嗯,但是
\uuuu main\uuuu
中没有引用吗?哦,是的:

sys.modules.pop('httplib')
gc.collect()
print len(gc.get_objects()) # 7063 objects in memory
万岁,放下300件物品。不过,没有雪茄,这是4000多件原始物品。 让我们尝试从副本还原系统模块

del httplib
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory
嗯,那是毫无意义的,没有变化。。 也许如果我们消灭地球人

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
怎么了。。如果我们
导入了
exec
中的模块,该怎么办

locals().clear()
import gc # we need this since gc was in globals() too
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory
现在,这不公平,它把它导入了
\uuuuu main\uuuuuu
,为什么?它不应该离开本地目录。。。啊!我们回到完全导入的
httplib
。 也许我们用一个虚拟物体来代替它

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

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
好的,在所有的尝试之后,最好是从起点开始+2675(接近+50%)。。。这只是一个模块的内容。。。里面甚至没有什么大东西

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


以更简单的复制形式提供完整源代码:

Python不支持卸载模块

但是,除非您的程序加载无限数量的模块,否则这不是内存泄漏的来源。模块通常在启动时加载一次,仅此而已。你的内存泄漏很可能在别处

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

(你应该试着写更简洁的问题;我只看了开头,略读了其余部分。)我在开头看到了一个简单的问题:

import httplib
for attr in dir(httplib):
    setattr(httplib, attr, None)
gc.collect()
print len(gc.get_objects())  # 6749 objects in memory
您创建了sys.modules的一个副本,因此现在您的副本有一个对该模块的引用——因此当然不会收集它。您可以使用gc.get\u referers查看引用它的内容

这很好:

sm = sys.modules.copy()

module1在我们从sys.modules中删除后立即卸载,因为没有对该模块的剩余引用。(导入后执行
module1=None
也会起作用——为了清晰起见,我将导入放在另一个函数中。您所要做的就是删除对它的引用。)

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

  • 为了收集模块,对模块的所有引用必须是不可访问的(就像收集任何对象一样)。这意味着导入它的任何其他模块也需要取消引用并重新加载
  • 如果您从sys.modules中删除了一个模块,而该模块仍然在其他地方被引用,那么您就创建了一个异常情况:该模块仍然由代码加载和使用,但模块加载程序不再知道它。下次导入模块时,将不会获得对现有模块的引用(因为您删除了该模块的记录),因此它将加载模块的第二个共存副本。这可能会导致严重的一致性问题。因此,在最终将模块从sys.modules中删除之前,请确保没有剩余的模块引用
在一般情况下,使用它有一些棘手的问题:检测哪些模块依赖于您正在卸载的模块;知道是否也可以卸载它们(很大程度上取决于您的用例);在检查所有这些时处理线程(查看imp.acquire_lock),等等


我可以设计一个这样做可能有用的例子,但大多数情况下,如果应用程序的代码发生变化,我建议重新启动应用程序。你可能只会让自己头疼。

我不太清楚Python,但在其他语言中,调用等价的
gc.collect()
不会释放未使用的内存-它只会在实际需要内存时释放该内存


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

Python的小对象管理器很少将内存返回到操作系统。
来自和。因此,严格地说,python(根据设计)有一种内存泄漏,即使对象是“gc收集的”。

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

# 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()

仅增长13%。如果你看看在新的
gc.get_objects
中加载的对象类型,其中一些是内置的、源代码、
random.
实用程序、
datetime
实用程序等。我在这里主要是作为@shuttle对话的更新,如果我们能取得更多进展,我将删除这些对象

是的,它确实加载了数量不受限制的模块——它是一个web应用程序服务器,它接受自己源代码的新版本并重新加载(这是相当标准的web任务)。泄漏的原因是旧代码仍然存在于内存中,即使被替换,即使无法访问……Python确实支持unloadi
# 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()
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