Работает ли Python GC с такими эталонными циклами?

Используя objgraph, я нашел несколько таких объектов:

Цикл состояния экземпляра

Будет ли сборщик мусора Python обрабатывать такие циклы или произойдет утечка?

Немного более широкий вид петли:

Более широкий обзор цикла InstanceState


person dbr    schedule 06.11.2011    source источник
comment
Релевантно: stackoverflow.com/q/15974561/1959808   -  person Ioannis Filippidis    schedule 08.10.2017


Ответы (4)


Стандартный механизм подсчета ссылок Python не может освобождать циклы, поэтому структура в вашем примере будет протекать.

Однако дополнительное средство сборки мусора включено по умолчанию и должно иметь возможность освобождать эту структуру, если ни один из ее компонентов больше не доступен извне и у них нет __del__() методов.

В противном случае сборщик мусора не будет не освободите их, потому что он не может определить безопасный порядок запуска этих __del__() методов.

person Frédéric Hamidi    schedule 06.11.2011
comment
Следует явно указать, что GC, который вы называете дополнительным, включен по умолчанию в Python. - person Eli Bendersky; 06.11.2011
comment
Ура! По крайней мере, люди (вы и Рэймонд Хеттингер), которые не путают сборщик мусора и счетчик ссылок и не используют сборщик мусора как синоним механизма подсчета ссылок! Меня долгое время недоумевал тот факт, что я понимаю такие вещи, но я постоянно читал тексты, в которых люди выражали свои идеи так, как будто ГК несет ответственность за убийство объектов, на которые больше нет ссылок. В вашем ответе разница прекрасно выражена и понятна. Спасибо. Отсюда +1 - person eyquem; 06.11.2011

Чтобы немного расширить ответ Фредерика, раздел "счетчики ссылок" документы хорошо объясняют обнаружение дополнительного цикла.

Поскольку я нахожу объяснение вещей хорошим способом подтвердить, что я это понимаю, вот несколько примеров... С этими двумя классами:

class WithDel(object):
    def __del__(self):
        print "deleting %s object at %s" % (self.__class__.__name__, id(self))


class NoDel(object):
    pass

Создание объекта и потеря ссылки из a вызывает срабатывание метода __del__ благодаря подсчету ссылок:

>>> a = WithDel()
>>> a = None  # leaving the WithDel object with no references 
deleting WithDel object at 4299615184

Если мы создадим эталонный цикл между двумя объектами без метода __del__, все по-прежнему будет без утечек, на этот раз благодаря обнаружению цикла. Во-первых, включите вывод отладки сборки мусора:

>>> import gc
>>> gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS)

Затем создайте цикл ссылок между двумя объектами:

>>> a = NoDel(); b = NoDel()
>>> a.other = b; b.other = a  # cyclical reference
>>> a = None; b = None # Leave only the reference-cycle
>>> gc.collect()
gc: collectable <NoDel 0x10046ed50>
gc: collectable <NoDel 0x10046ed90>
gc: collectable <dict 0x100376c20>
gc: collectable <dict 0x100376b00>
4
>>> gc.garbage
[]

(dict взят из внутреннего атрибута __dict__ объектов)

Все в порядке, пока хотя бы один из объектов в цикле содержит метод __del__:

>>> a = NoDel(); b = WithDel()
>>> a.other = b; b.other = a
>>> a = None; b = None
>>> gc.collect()
gc: uncollectable <WithDel 0x10046edd0>
gc: uncollectable <dict 0x100376b00>
gc: uncollectable <NoDel 0x10046ed90>
gc: uncollectable <dict 0x100376c20>
4
>>> gc.garbage
[<__main__.WithDel object at 0x10046edd0>]

Как упомянул Пол, цикл можно разорвать с помощью weakref:

>>> import weakref
>>> a = NoDel(); b = WithDel()
>>> a.other = weakref.ref(b)
>>> b.other = a # could also be a weakref

Затем, когда ссылка b на объект WithDel теряется, он удаляется, несмотря на цикл:

>>> b = None
deleting WithDel object at 4299656848
>>> a.other
<weakref at 0x10045b9f0; dead>

О, objgraph был бы полезен указал проблемный __del__ метод следующим образом

person dbr    schedule 06.11.2011
comment
Это работает для меня в python2, однако в python3 я не могу воспроизвести утечку... Что-то изменилось? - person kralyk; 27.01.2017
comment
@kralyk Я предполагаю, что за это отвечает этот человек: python.org/dev/peps/pep- 0442 - person ead; 16.01.2018

Сборщик мусора в Python предназначен для обхода всех живых объектов для обнаружения и устранения циклов ссылок без внешних ссылок.

Вы можете убедиться в этом, запустив gc.collect(), а затем напечатав gc.garbage и gc. получить_объекты.

person Raymond Hettinger    schedule 06.11.2011

Если вы используете weakrefs для своих родительских указателей, то GC будет происходить нормально.

person PaulMcG    schedule 06.11.2011