在一种情况下,只要第一个对象存在,我就希望维持从一个对象到另一个对象的映射。我的第一个想法是使用WeakKeyDictionary。

import weakref
import gc

class M:
    pass

w = weakref.WeakKeyDictionary()
m = M()
w[m] = some_other_object
del m
gc.collect()
print w.keys()

在大多数情况下,这可以正常工作。但是,在some_other_objectm(或对其具有引用)的情况下,M实例将不会被垃圾回收。 (要查看示例,请将some_other_object替换为m)

当然,如果我将映射存储在对象本身上,则在删除它时将被垃圾回收:
import weakref
import gc

class M:
    pass

m = M()
m.circular_reference = m
r = weakref.ref(m)
del m
gc.collect()
print r

我可以使用weakref模块获得第二个示例的结果吗(即,无需更改m)?

换句话说,我可以使用weakref模块将一个对象映射到自身(或另一个具有强烈引用的对象),并且仅在该对象还有其他引用的情况下才将其保留在内存中吗?

最佳答案

在这些示例中,您实际上没有循环引用。您的圈子如下所示:

WeakKeyDict->值->键-> ...

因此,该dict使值保持事件状态,这又使 key 保持事件状态,并且通过不涉及强引用的单独机制, key 指示dict使该值保持事件状态。由于此处没有适当的引用圈,因此GC永远不会检测到任何问题。 (但是,您的第二个示例确实包含一个循环引用,这就是为什么它的行为类似于第一个示例的原因)

我怀疑您问题的唯一解决方案是确保dict的值永远不会对dict中的任何键有强引用(直接或间接)。这类似于srgerg提出的内容,但您实际上希望对键的引用是弱的,而不是对所有值进行弱引用。例如:

import weakref
import gc

class M:
    pass

w = weakref.WeakKeyDictionary()
key = M()
val = M()
val.keyRef = weakref.ref(val)
w[key] = val
print len(w)   ## prints 1
del key
gc.collect()
print len(w)   ## prints 0

这样,总是有对这些值的强引用,但是您正在仔细地控制对键的引用,以确定从dict中删除了什么。根据您程序的复杂程度,这可能会很耗时,因为您需要手动跟踪所有对键的引用。

但是,如果您告诉我们更多有关特定问题的信息,也许我们可以提出一个更简单的解决方案。

关于python - GC不会在WeakKeyDictionaries中删除循环引用吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6210024/

10-12 16:50
查看更多