内部引用防止垃圾回收

7

我正在编写一个简单的平台游戏,发现在移除“ghost”实例时,它们会继续存在并且无法被垃圾回收。尽管我已经删除了所有引用,但是这些幽灵对象似乎有一些内部引用阻止了它们被垃圾回收。具体来说,它们有一些作为方法开关的属性。

下面的代码展示了我的问题:

import weakref

weak_ghosts = weakref.WeakKeyDictionary()

class Ghost(object):
    def __init__(self):
        #pass
        self.switch = {'eat':self.eat, 'sleep':self.sleep}

    def eat(self):
        pass

    def sleep(self):
        pass

ghost = Ghost()
weak_ghosts[ghost] = None
#ghost.switch = {}    # uncomment this line and ghost is successfully removed
del ghost
print "number of ghosts =", len(weak_ghosts)

#output:
number of ghosts = 1

问题:

  1. 到底发生了什么事情?
  2. 我应该怎么做才能避免这种情况?
  3. 我是否使用了正确的方法来制作可切换的方法字典?
2个回答

4

由于self.switch引用了它所属的对象,因此创建了一个循环引用。请看下面的示例:

import weakref

class Ghost(object):
    def __init__(self):
        #pass
        self.switch = {'eat':self.eat, 'sleep':self.sleep}

    def eat(self):
        pass

    def sleep(self):
        pass

ghost = Ghost()

def callback(o):
    print 'callback', o

wref = weakref.ref(ghost, callback)
print 'del ghost'
del ghost
print 'after del ghost'

输出:

del ghost
after del ghost
callback <weakref at 00B55FC0; dead>

因此,实际对象只是在关闭时进行了清理。

您可以手动运行GC以查看效果。在上面的脚本末尾添加以下内容:

print 'gc.collect'
import gc
gc.collect()
print 'after gc.collect'

现在你会看到:
del ghost
after del ghost
gc.collect
callback <weakref at 00B55FC0; dead>
after gc.collect

请注意,默认情况下,此垃圾收集器已启用,并将定期运行。 它将清理您的ghost对象,因为它们成为不可达的循环引用。

2

一个选择是这样做:

class Ghost(object):
    def __init__(self):
        self.switch = {'eat':Ghost.eat, 'sleep':Ghost.sleep}

所以这些方法被保持为未绑定状态。

1
但是请记住,在实际调用它们时,要明确地传递对象。 - Eli Bendersky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接