python - reload()时gc不可达
我有一段代码,保存为 so.py:
import gc
gc.set_debug(gc.DEBUG_STATS|gc.DEBUG_LEAK)
class GUI():
#########################################
def set_func(self):
self.functions = {}
self.functions[100] = self.userInput
#########################################
def userInput(self):
a = 1
g = GUI()
g.set_func()
print gc.collect()
print gc.garbage
这是运行后的输出:

我有两个问题:
为什么在第一次导入时,gc.collect() 不报告不可达?而是只有在 reload() 时才报告不可达。
有没有什么快速的方法来修复这个函数映射的循环引用,比如 self.functions[100] = self.userInput?因为我以前的项目有很多这样的循环引用,我在寻找一种快速的方法或者一行代码来修改这些代码。目前我做的是在最后对所有这些函数使用 "del g.functions"。
2 个回答
reload
的作用是重新执行模块。新的定义会替代旧的定义,这样旧的值就无法再被访问了。相比之下,在第一次导入时,没有被替代的定义,所以自然也就没有什么会变得无法访问。一种方法是将
functions
对象作为参数传递给set_func
,而不是将其作为实例属性来赋值。这样可以打破循环,同时仍然可以将functions
对象传递到需要的地方。
第一次导入这个模块时,什么都不会被清理,因为你有一个指向
so
模块的引用,所有其他对象都是通过它来引用的,所以它们都还在,垃圾回收器没有东西可以清理。当你执行
reload(so)
时,模块会重新运行,这样就覆盖了之前的所有引用,因此现在那些旧值就没有任何引用了。在这里你确实有一个引用循环:
self.functions[100] = self.userInput
因为
self.userInput
是一个绑定的方法,它有一个指向self
的引用。所以现在self
指向functions
字典,而这个字典又指向userInput
这个绑定的方法,这个方法又指向self
,所以垃圾回收器会清理这些对象。这要看你想做什么。从你的代码来看,不太清楚你是怎么使用
self.functions
这个字典的,具体情况可能有不同的解决方案。打破循环的最简单方法就是不创建
self.functions
属性,而是明确地传递这个字典。如果
self.functions
只引用绑定的方法,你可以存储方法的名称而不是方法本身:self.functions[100] = self.userInput.__name__
然后你可以通过以下方式调用这个方法:
getattr(self, self.functions[100])()
或者你可以这样做:
from operator import methodcaller call_method = methodcaller(self.functions[100]) call_method(self) # calls self.userInput()
我不太明白你说的“目前我做的是在最后用del g.functions
删除所有这些函数。”你指的是哪些函数呢?
另外,这真的是个问题吗?你真的遇到了内存泄漏吗?
请注意,垃圾回收器报告对象为不可达而不是不可回收。这意味着即使这些对象是引用循环的一部分,它们也会被释放。所以不应该发生内存泄漏。
实际上,添加del g.functions
是没用的,因为这些对象反正会被释放,所以最简单的解决办法就是直接删除所有这些del
语句,因为它们根本没有任何作用。
它们被放入gc.garbage
中是因为gc.DEBUG_LEAK
意味着启用了GC.DEBUG_SAVEALL
这个标志,这会让垃圾回收器把所有不可达的对象放入garbage
中,而不仅仅是不可回收的对象。