Saj*_*put 9 python garbage-collection weak-references
我在David Beazley的Python Essential Reference中读到了一个例子:
class Account(object):
def __init__(self,name,balance):
self.name = name
self.balance = balance
self.observers = set()
def __del__(self):
for ob in self.observers:
ob.close()
del self.observers
def register(self,observer):
self.observers.add(observer)
def unregister(self,observer):
self.observers.remove(observer)
def notify(self):
for ob in self.observers:
ob.update()
def withdraw(self,amt):
self.balance -= amt
self.notify()
class AccountObserver(object):
def __init__(self, theaccount):
self.theaccount = theaccount
theaccount.register(self)
def __del__(self):
self.theaccount.unregister(self)
del self.theaccount
def update(self):
print("Balance is %0.2f" % self.theaccount.balance)
def close(self):
print("Account no longer in use")
# Example setup
a = Account('Dave',1000.00)
a_ob = AccountObserver(a)
Run Code Online (Sandbox Code Playgroud)
有人提到
...这些类创建了一个引用循环,其中引用计数永远不会下降到0并且没有清理.不仅如此,垃圾收集器(
gc模块)甚至不会清理它,导致永久性内存泄漏.
有谁可以解释这是怎么发生的?弱推理如何在这里有所帮助?
Mar*_*ers 10
Account().observers是一组参考AccountObserver()实例,但AccountObserver().theaccount是指向一个参考回至Account()其中观察者被存储在该组实例.这是一个循环参考.
通常,垃圾收集器将检测此类圆圈并打破循环,允许引用计数降至0并进行正常清理.但是,对于定义__del__方法的类,有一个例外,正如David的示例中的类所做的那样.从Python 2 gc模块文档:
gc.garbage
收集器发现无法访问但无法释放的对象列表(无法收集的对象).默认情况下,此列表仅包含具有__del__()方法的对象.具有__del__()方法并且是参考循环的一部分的对象导致整个参考循环无法收集,包括不一定在循环中但仅可从其中访问的对象.Python不会自动收集这样的循环,因为一般来说,Python不可能猜测运行__del__()方法的安全顺序.
所以圆圈不能被破坏,因为垃圾收集器拒绝猜测__del__首先调用的终结器(方法).请注意,随机选择一个对于特定示例并不安全 ; 如果你先拨打电话Account().__del__,那么该observers组将被删除,后续的电话AccountObserver().__del__将失败AttributeError.
弱引用不参与引用计数; 因此,如果AccountObserver().theaccount使用弱引用来指向相应的Account()实例,那么Account()如果只剩下弱引用,则实例将不会保持活动状态:
class AccountObserver(object):
def __init__(self, theaccount):
self.theaccountref = weakref.ref(theaccount)
theaccount.register(self)
def __del__(self):
theaccount = self.theaccountref()
if theaccount is not None:
theaccount.unregister(self)
def update(self):
theaccount = self.theaccountref()
print("Balance is %0.2f" % theaccount.balance)
def close(self):
print("Account no longer in use")
Run Code Online (Sandbox Code Playgroud)
请注意,我链接到Python 2文档.从Python 3.4开始,这不再是真的,即使是PEP 442 -已经实现了安全对象终结,也会清除示例中显示的循环依赖关系:
此PEP的主要优点是将对象与终结器相关联,例如带有
__del__方法的对象和带有finally块的生成器.现在可以在参考周期中回收这些对象.
并不是说这不会导致追溯; 如果您在Python 3.6中执行该示例,删除引用,并启动垃圾回收运行,您将获得回溯,因为该Account().observers集可能已被删除:
>>> import gc
>>> del a, a_ob
>>> gc.collect()
Account no longer in use
Exception ignored in: <bound method AccountObserver.__del__ of <__main__.AccountObserver object at 0x10e36a898>>
Traceback (most recent call last):
File "<stdin>", line 6, in __del__
File "<stdin>", line 13, in unregister
AttributeError: 'Account' object has no attribute 'observers'
65
Run Code Online (Sandbox Code Playgroud)
回溯只是一个警告,否则,gc.collect()调用成功,无论如何都会获得僵尸AccountObserver()和Account()对象.