2010-03-11 3 views
31

Предположим, у меня есть код, который поддерживает структуру parent/children. В такой структуре я получаю круговые ссылки, где ребенок указывает на родителя, а родитель указывает на ребенка. Должен ли я беспокоиться о них? Я использую Python 2.5.Должен ли я беспокоиться о круговых ссылках на Python?

Я обеспокоен тем, что они не будут собирать мусор, и приложение в конечном итоге уничтожит всю память.

ответ

25

«Беспокойство» неуместно, но если ваша программа оказывается медленно, потребляет больше памяти, чем ожидался, или странные необъяснимым паузы, причина, скорее всего, будет в этих циклах обработки мусора - они должны быть мусором, собранным другой процедурой, чем «нормальные» (ациклические) ссылочные графики, и эта коллекция случайна и может быть медленной, если у вас есть много объектов, связанных в таких циклах (циклическая сборка мусора также блокируется, если объект в цикле имеет специальный метод __del__).

Таким образом, эталонные петли не повлияют на правильность вашей программы, но могут повлиять на ее производительность и/или площадь.

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

Если вы хотите использовать более прямой контроль (или выполнить отладку, посмотрите, что именно происходит) в отношении циклической сборки мусора, используйте модуль gc в стандартной библиотеке Python.

+0

Плюс 1 для заметки о '__del__'. Если у ваших деструкторов объектов есть побочные эффекты, тогда вам может понадобиться немного подумать о циклических ссылках (и когда все будет уничтожено). – speedplane

9

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

+0

Предполагая, что, конечно, нет '__del__' методы. Которых обычно не должно быть, но вы никогда не знаете. Некоторое время даже «коллекции.OrderedDict» были по какой-то причине. – Antimony

15

Экспериментально: вы хорошо:

import itertools 

for i in itertools.count(): 
    a = {} 
    b = {"a":a} 
    a["b"] = b 

Он постоянно остается при использовании 3,6   МБ оперативной памяти.

+2

Прохладный! Тогда я в безопасности. :) – bodacydo

+0

Какую версию вы использовали? –

+0

@SargeBorsch CPython 2.что-то. Я предполагаю, что любая из основных реализаций будет вести себя одинаково. – cobbal

5

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

+0

Спасибо, Колин. Я не знал, что они «нормальные». Они казались мне очень особенными. Но теперь я узнал иначе. :) – bodacydo

+0

Кроме того, они, очевидно, необходимы для графиков. – Antimony

3

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

while True: 
    a=range(100) 
    b=range(100) 
    a.append(b) 
    b.append(a) 
    a.append(a) 
    b.append(b) 
+0

Спасибо, что написали этот тестовый код. Я не думал об этом. – bodacydo

+0

Разве вы не подразумеваете 'a.extend (b)', а не 'append'? – richizy

+4

@richizy Я действительно имею в виду append, потому что хочу сохранить ссылку на a и b внутри a и b, а не на значения. Таким образом, будет выполняться циклическая ссылка. – douglaz

1

Кажется, что проблема связана со ссылками на методы в списках в переменной. Вот два примера. Первый не звонит __del__. Второй со слабымref нормально для __del__. Однако, в этом последнем случае проблема заключается в том, что вы не можете ссылаться на слабо методы: http://docs.python.org/2/library/weakref.html

import sys, weakref 

class One(): 
    def __init__(self): 
     self.counters = [ self.count ] 
    def __del__(self): 
     print("__del__ called") 
    def count(self): 
     print(sys.getrefcount(self)) 


sys.getrefcount(One) 
one = One() 
sys.getrefcount(One) 
del one 
sys.getrefcount(One) 


class Two(): 
    def __init__(self): 
     self.counters = [ weakref.ref(self.count) ] 
    def __del__(self): 
     print("__del__ called") 
    def count(self): 
     print(sys.getrefcount(self)) 


sys.getrefcount(Two) 
two = Two() 
sys.getrefcount(Two) 
del two 
sys.getrefcount(Two) 
Смежные вопросы