2010-03-11 14 views
31

Supponiamo che abbia un codice che mantiene una struttura padre/figlio. In tale struttura ottengo riferimenti circolari, in cui un bambino indica un genitore e un genitore indica un bambino. Dovrei preoccuparmi di loro? Sto usando Python 2.5.Dovrei preoccuparmi dei riferimenti circolari in Python?

Sono preoccupato che non saranno raccolti e l'applicazione alla fine consumerà tutta la memoria.

risposta

25

"preoccupazione" è fuori luogo, ma se il programma risulta essere lenta, consumare più memoria del previsto, o hanno strano inspiegabile pause, la causa è davvero probabile che si trovi in ​​quei loop di riferimento della spazzatura: devono essere raccolti con una procedura diversa dai grafici di riferimento "normali" (aciclici), e quella raccolta è occasionale e può essere lenta se si dispone di molto di oggetti legati in tali loop (la raccolta ciclico-spazzatura è anche inibita se un oggetto nel ciclo ha un metodo speciale __del__).

Pertanto, i loop di riferimento non influiscono sulla correttezza del programma, ma possono influire sulle sue prestazioni e/o sul footprint.

Se e quando si desidera rimuovere loop indesiderati di riferimenti, è possibile utilizzare spesso il modulo weakref nella libreria standard di Python.

Se e quando si desidera esercitare un controllo più diretto (o eseguire il debug, vedere cosa sta accadendo esattamente) riguardo la garbage collection ciclica, utilizzare il modulo gc nella libreria standard di Python.

+0

Più 1 per la nota su '__del__'. Se i tuoi distruttori di oggetti hanno effetti collaterali, allora potresti voler pensare a riferimenti ciclici (e quando le cose vengono distrutte) un po 'più attentamente. – speedplane

9

Python rileva il ciclo e rilascia la memoria quando non ci sono riferimenti esterni.

+0

Supponendo, naturalmente, ci sono metodi '__del__'. Che normalmente non dovrebbe essere, ma non si sa mai. Per un po ', anche 'collections.OrderedDict' ne aveva uno per qualche motivo. – Antimony

15

Sperimentalmente: Stai bene:

import itertools 

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

si mantenne sempre a utilizzare 3,6   MB di RAM.

+2

Fantastico! Allora sono al sicuro. :) – bodacydo

+0

Quale implementazione hai usato? –

+0

@SargeBorsch CPython 2.qualcosa. Immagino che qualsiasi delle principali implementazioni si comporterebbe allo stesso modo però. – cobbal

5

I riferimenti circolari sono una cosa normale da fare, quindi non vedo un motivo per essere preoccupati per loro. Molti algoritmi ad albero richiedono che ogni nodo abbia collegamenti ai suoi figli e ai suoi genitori. Devono anche implementare qualcosa come una lista doppiamente collegata.

+0

Grazie Colin. Non sapevo che fossero "una cosa normale". Mi sembravano molto speciali. Ma ora ho imparato il contrario. :) – bodacydo

+0

Inoltre, sono ovviamente necessari per i grafici. – Antimony

3

Non penso che dovresti preoccuparti. Provate il seguente programma e si vede che non consumerà tutta la memoria:

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

Grazie per aver scritto questo codice di prova. Non ci ho pensato. – bodacydo

+0

non intendete 'a.extend (b)', non 'append'? – richizy

+4

@richizy Intendo davvero append perché voglio salvare il riferimento ad aeb all'interno di aeb, non i valori. In questo modo avverrà il riferimento circolare. – douglaz

1

Sembra esserci un problema con i riferimenti ai metodi negli elenchi in una variabile. Ecco due esempi. Il primo non chiama __del__. Il secondo con weakref è ok per __del__. Tuttavia, in questo caso, dopo il problema è che non si può debolmente riferimento metodi: 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) 
Problemi correlati