我有这个代码,另存为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”。
答案 0 :(得分:5)
第一次导入模块时,没有收集到任何内容,因为您有so
模块的引用,并且所有其他对象都被它引用,因此它们都处于活动状态且垃圾收集器没有任何内容收集。
当您reload(so)
发生的事情是重新执行该模块时,覆盖所有先前的引用,因此现在旧值不再有任何引用。
你做的参考周期是:
self.functions[100] = self.userInput
由于self.userInput
是绑定方法,因此它引用了self
。所以现在self
引用了functions
字典,该字典引用了userInput
绑定方法,该方法引用了self
和gc
将收集这些对象。
这取决于你想要做什么。从您的代码中不清楚您如何使用该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
,这使得收集器将所有无法访问的对象放入{{1}而不仅仅是无法收集的。
答案 1 :(得分:1)
reload
的本质是重新执行模块。新定义取代旧定义,因此旧值无法访问。相比之下,在第一次导入时,没有被取代的定义,所以自然没有什么可以变得无法到达。functions
对象作为参数传递给set_func
,而不将其指定为实例属性。这将打破循环,同时仍允许您将functions
对象传递到所需的位置。