Я пытаюсь понять внутренности сборщика "мусора" CPython, конкретно когда деструктор называют. До сих пор поведение интуитивно, но следующий случай сбивает меня с толку:
Я думал, что это только произошло бы, если бы сборщик "мусора" был включен. Кто-то может объяснить, почему это происходит? Существует ли способ задержать вызов деструктора?
import gc
import unittest
_destroyed = False
class MyClass(object):
def __del__(self):
global _destroyed
_destroyed = True
class GarbageCollectionTest(unittest.TestCase):
def testExplicitGarbageCollection(self):
gc.disable()
ref = MyClass()
ref = None
# The next test fails.
# The object is automatically destroyed even with the collector turned off.
self.assertFalse(_destroyed)
gc.collect()
self.assertTrue(_destroyed)
if __name__=='__main__':
unittest.main()
Отказ от ответственности: этот код не предназначен для производства - я уже отметил, что это является очень определенным для реализации и не работает над Jython.
Python имеет как сборку мусора с подсчетом ссылок , так и циклическую сборку мусора, и последнее, что ] gc
модуль управления. Подсчет ссылок нельзя отключить, и, следовательно, он все еще происходит, когда циклический сборщик мусора выключен.
Поскольку после ref = None
не осталось ссылок на ваш объект, его метод __ del __
вызывается в результате обнуления его счетчика ссылок.
В документации есть подсказка: «Поскольку сборщик дополняет подсчет ссылок, уже используемый в Python ...» (выделено мной).
Вы можете остановить срабатывание первого утверждения, заставив объект ссылаться на себя, чтобы его счетчик ссылок не обнулялся, например, передав ему этот конструктор:
def __init__(self):
self.myself = self
Но если вы это сделаете, второе утверждение сработает. Это потому, что циклы мусора с методами __ del __
не собираются - см. Документацию для gc.garbage .
В зависимости от вашего определения сборщика мусора, CPython имеет два сборщика мусора: один, подсчитывающий ссылки, и другой.
Счетчик ссылок всегда работает, и его нельзя отключить, так как он довольно быстрый и легкий, который не оказывает существенного влияния на время работы системы.
Другой (по-моему, с некоторыми вариантами mark и sweep) запускается время от времени, и его можно отключить. Это связано с тем, что для этого требуется, чтобы интерпретатор был приостановлен во время его работы, а это может произойти в неподходящий момент и потреблять довольно много времени ЦП.
Эта возможность отключить его существует в то время, когда вы планируете делать что-то критичное по времени, и отсутствие этого GC не вызовет у вас никаких проблем.
Документы здесь объясняют, как то, что называется «необязательный сборщик мусора», на самом деле является сборщиком циклического мусора. (такой, который не учитывается при подсчете ссылок). Подсчет ссылок объясняется здесь , с намеком на его взаимодействие с циклическим gc:
Хотя Python использует традиционную реализацию подсчета ссылок , он также предлагает цикл детектор, который работает для обнаружения эталонных циклов. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок; это слабость сборки мусора, реализованной с использованием только подсчета ссылок. Циклы ссылок состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет счетчик ссылок, который {{1} }} не равно нулю. Типичные реализации подсчета ссылок не могут освободить память, принадлежащую каким-либо объектам в цикле ссылок или , на которые ссылаются объекты в {{1} }} цикл, даже если нет никаких дополнительных ссылок на сам цикл .