Существует 2 способа сборки мусора и питон использует их оба одновременно.
reference counting
- первый способ подсчет ссылок, когда количество ссылок на объект падает до нуля, этот объект удаляется из памяти, это удобно и происходит сразу как количество ссылок доходит до нуля, имеет и недостатки.
Во-первых, количество ссылок на объект надо где-то хранить что занимает память.
Во вторых присвоение или удаление происходит при каждой операции, то есть занимает время выполнения программы.
В третьих сборщик мусора ну учитывает циклические ссылки, которые всегда ссылаются др на др и удалить их не представляется возможным, ибо количество ссылок никогда не упадет до нуля.
Циклическую ссылку нельзя создать при помощи 2 объектов, требуется минимум 3, один как точка входа в цикл и 2 других как сам цикл:
class Cicle:
def __init__(self, name):
self.name = name
def set_link(self, value):
self.value = value
main = Cicle('main')
left = Cicle('left')
right = Cicle('right')
main.set_link('left')
left.set_link('right')
right.set_link('left')
print(main.__dict__)
print(left.__dict__)
print(right.__dict__)
# Вывод
# {'name': 'main', 'value': 'left'}
# {'name': 'left', 'value': 'right'}
# {'name': 'right', 'value': 'left'}
Видим как два объекта left
и right
ссылаются друг на друга, это и
есть циклическая ссылка. Мы можем удалить внешние ссылки на сами
объекты, но внутренние ссылки объектов др на др останутся, они не
удаляются, но будут существовать и не будут доступны из вне ибо
ссылки на внешний объект уже пропал.
tracing
- второй способ сбора мусора, суть его заключается в том что берется объект и из него происходит проход по всем доступным ссылкам.
Если находятся объекты, которые не имеют доступ по ссылкам из вне, то они удаляются, так и отслеживаются циклические ссылки.
Еще пример циклических ссылок Имеем 2 списка, вносим один список в другой список, и вот получается кольцевая ссылка, которую можно разрушить только в ручную, сам сборщик мусора ее не уберет.
# Пример циклич-х ссылок
a = []
b = []
a.append(b)
b.append(a)