Почему деструктор вызывается, когда сборщик мусора CPython отключен? - PullRequest
6 голосов
/ 05 апреля 2010

Я пытаюсь понять внутренности сборщика мусора CPython, особенно когда вызывается деструктор. Пока что поведение интуитивно понятно, но меня сбивает с толку следующий случай:

  1. Отключить ГХ.
  2. Создайте объект, затем удалите ссылку на него.
  3. Объект уничтожен и вызывается метод _____del_____.

Я думал, что это произойдет, только если включен сборщик мусора. Может кто-нибудь объяснить, почему это происходит? Есть ли способ отложить вызов деструктора?

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.

Ответы [ 3 ]

9 голосов
/ 05 апреля 2010

Python имеет как подсчет ссылок сборщик мусора, так и циклический сборщик мусора, и именно последний контролирует модуль gc. Подсчет ссылок не может быть отключен, и, следовательно, все еще происходит, когда циклический сборщик мусора выключен.

Поскольку после ref = None не осталось ссылок на ваш объект, его метод __del__ вызывается в результате того, что счетчик ссылок становится равным нулю.

В документации есть подсказка : "Поскольку сборщик дополняет подсчет ссылок, уже используемый в Python ..." (мой акцент).

Вы можете остановить срабатывание первого утверждения, заставив объект ссылаться на себя, чтобы его счетчик ссылок не обнулялся, например, задав ему этот конструктор:

def __init__(self):
    self.myself = self

Но если вы сделаете это, сработает второе утверждение. Это связано с тем, что циклы мусора с __del__ методами не собираются - см. Документацию для gc.garbage .

4 голосов
/ 05 апреля 2010

Документы здесь объясняют, как то, что называется "необязательным сборщиком мусора", на самом деле является сборщиком циклического мусора (такого, который подсчет ссылок не поймает). Подсчет ссылок объясняется здесь , с намеком на его взаимодействие с циклическим gc:

В то время как Python использует традиционные реализация подсчета ссылок, это также предлагает детектор цикла, который работает для обнаружения опорных циклов. это позволяет приложениям не беспокоиться о создание прямого или косвенного циркуляра Рекомендации; это слабость сборка мусора реализована с использованием только подсчет ссылок. Ссылка циклы состоят из объектов, которые содержать (возможно косвенные) ссылки для себя, так что каждый объект в цикл имеет счетчик ссылок, который ненулевой Типичная ссылка подсчет реализации не умеет вернуть память, принадлежащую любому объекты в ссылочном цикле, или ссылки на объекты в цикл, хотя нет дальнейшие ссылки на цикл сам по себе.

4 голосов
/ 05 апреля 2010

В зависимости от вашего определения сборщика мусора, CPython имеет два сборщика мусора, один подсчитывает ссылки и другой.
Счетчик ссылок всегда работает и не может быть выключен, так как он достаточно быстрый и легкий, который не оказывает существенного влияния на время работы системы.
Другой (какой-то вариант метки и развертки, я думаю) запускается очень часто и может быть отключен. Это связано с тем, что переводчик должен быть приостановлен во время работы, а это может произойти в неподходящий момент и потреблять довольно много процессорного времени.
Эта возможность отключить его есть в то время, когда вы ожидаете делать что-то, что критично ко времени, и отсутствие этого GC не вызовет у вас никаких проблем.

...