【问题标题】:Garbage collector and problems with the __del__ finalizer垃圾收集器和 __del__ 终结器的问题
【发布时间】:2014-07-09 06:30:07
【问题描述】:

在网上冲浪 (here) 我发现使用垃圾收集器的__del__ 方法收集对象存在一些问题。
我的疑问很简单:为什么?

根据文档:

具有__del__() 方法并且属于引用循环的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从循环中访问的对象。 Python 不会自动收集此类循环,因为一般情况下,Python 无法猜测运行 __del__() 方法的安全顺序。

为什么__del__ 方法这么有问题?实现它的对象和不实现它的对象有什么区别?它只会破坏一个实例。

【问题讨论】:

  • 这个案例在Martijn's answer to your previous question中有专门的介绍;垃圾收集器确实“检测到它并回收内存”。是什么让你认为它没有?
  • 是的,我知道,但根据我发布的链接:'由于没有很好的解决方案来解决这个问题,所以从具有终结器的对象引用的循环不会被释放。而是将这些对象添加到无法收集的垃圾的全局列表中。'
  • 但是列表没有终结者...阅读this
  • @jonrsharpe 您链接的上一个答案没有回答这个问题。该答案解释了 Python 的引用计数及其循环中断器,但不包括 __del__ 被明确排除在循环中断之外的对象,这就是这个问题的意义所在。
  • @user4815162342 您应该查看编辑 - 问题已更改

标签: python garbage-collection


【解决方案1】:

__del__ 不会销毁实例,一旦它的引用计数达到零,它就会被 python 运行时自动销毁。 __del__ 允许您连接到该进程并执行其他操作,例如释放与对象关联的外部资源。

危险在于附加操作甚至可能复活对象 - 例如,通过将其存储到全局容器中。在这种情况下,销毁被有效地取消(直到下一次对象的引用计数降至零)。正是这种情况导致__del__ 的存在将对象从循环断路器(也称为垃圾收集器)管理的对象中排除。如果收集器在循环中的所有对象上调用__del__,并且其中一个决定复活该对象,这将需要复活整个循环——这是不可能的,因为已经调用了其他循环成员的__del__方法,可能对其对象造成永久性损坏(例如,通过释放外部资源,如上所述)。

如果您只需要收到对象销毁的通知,请使用weakref.ref。如果您的对象与需要释放的外部资源相关联,请实现close 方法和/或上下文管理器接口。使用__del__ 几乎没有正当理由。

【讨论】:

  • 我了解具有循环引用的对象,但这让我想到......假设我有一个简单的对象,当它的引用计数器降至零时,它不包含循环引用 __del__ 方法是调用。如果在__del__ 方法中通过将其存储到全局容器中来复活它,那么它不能被释放,但它会,因为它的引用计数器为零。所以即使没有包含循环引用的对象,我们有问题吗?
  • @antox 一旦存储到全局容器中,它的引用计数就不再为零。因此,算法类似于(非常简化):if (obj->refcnt == 0) { run___del__(obj); if (obj->refcnt == 0) free(obj); }。有关血腥细节,请参阅the source
  • 我想到了 1 个问题。如果我在 2 个或更多对象之间有循环引用,则上述问题仅存在于 2 个或更多对象定义了 __del__ 方法,但如果只有 1 个对象具有 __del__ 而其他对象没有,我认为没有收集它们的问题。对吗?
  • @antox 不,问题完全一样。复活对象需要复活循环的所有成员,如果它们已经被循环中断器清除并通过调用free() 回收,则无法完成。
  • free() 方法包含在哪里?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 2012-03-10
  • 2015-03-14
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多