python - 重载()时无法访问gc

时间:2014-08-26 09:26:44

标签: python python-2.7 garbage-collection circular-reference

我有这个代码,另存为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

这是输出: enter image description here

我有两个问题:

  1. 为什么gc.collect()在首次导入时不会报告无法访问?相反,它仅在reload()时报告无法访问

  2. 有没有快速的方法来修复这个函数映射循环引用,即self.functions [100] = self.userInput?因为我的旧项目有很多这个函数映射循环引用,我正在寻找一个快速的方法/一行来改变这些代码。目前我所做的是最后所有这些功能的“del g.functions”。

2 个答案:

答案 0 :(得分:5)

  1. 第一次导入模块时,没有收集到任何内容,因为您有so模块的引用,并且所有其他对象都被它引用,因此它们都处于活动状态且垃圾收集器没有任何内容收集。

    当您reload(so)发生的事情是重新执行该模块时,覆盖所有先前的引用,因此现在旧值不再有任何引用。

    的参考周期是:

     self.functions[100] = self.userInput
    

    由于self.userInput是绑定方法,因此它引用了self。所以现在self引用了functions字典,该字典引用了userInput 绑定方法,该方法引用了selfgc将收集这些对象。

  2. 这取决于你想要做什么。从您的代码中不清楚您如何使用该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()
    
  3. 我真的不明白你的意思是“目前我所做的就是del g.functions最后所有这些功能。”你在谈论哪些功能?

    另外,这真的是个问题吗?您是否遇到过真正的内存泄漏?

    请注意,垃圾收集器将对象报告为无法访问而不是无法收集。这意味着即使它们是参考周期的一部分,也会释放对象。所以不应该发生内存泄漏。

    实际上添加del g.functions 无用因为无论如何都会释放对象,所以一行修复只是删除所有del语句,因为它们什么都不做。

    将它们放入gc.garbage这一事实是因为gc.DEBUG_LEAK意味着标记GC.DEBUG_SAVEALL,这使得收集器将所有无法访问的对象放入{{1}而不仅仅是无法收集的

答案 1 :(得分:1)

  1. reload的本质是重新执行模块。新定义取代旧定义,因此旧值无法访问。相比之下,在第一次导入时,没有被取代的定义,所以自然没有什么可以变得无法到达。
  2. 一种方法是将functions对象作为参数传递给set_func,而不将其指定为实例属性。这将打破循环,同时仍允许您将functions对象传递到所需的位置。