当前位置:  开发笔记 > 编程语言 > 正文

__del __()如何干扰垃圾收集?

如何解决《__del__()如何干扰垃圾收集?》经验,为你挑选了1个好方法。

我在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)

有人提到

...这些类创建了一个引用循环,其中引用计数永远不会下降到0并且没有清理.不仅如此,垃圾收集器(gc模块)甚至不会清理它,导致永久性内存泄漏.

有谁可以解释这是怎么发生的?弱推理如何在这里有所帮助?



1> Martijn Piet..:

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")

请注意,我链接到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: >
Traceback (most recent call last):
  File "", line 6, in __del__
  File "", line 13, in unregister
AttributeError: 'Account' object has no attribute 'observers'
65

回溯只是一个警告,否则,gc.collect()调用成功,无论如何都会获得僵尸AccountObserver()Account()对象.

推荐阅读
云聪京初瑞子_617
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有