Contador de referencias.
Chema Cortés
py en ch3m4.org
Lun Mayo 10 19:01:14 CEST 2004
El Lunes, 10 de Mayo de 2004 15:03, Pepe Aracil escribió:
> Tengo algunas preguntillas para quedarme tranquilo si alguna vez me dejo
> referencias circulares sin depurar.
>
> ¿Se encarga el gc de purgar todos los objetos a los que ya no se puede
> acceder o tendré que lanzar un proceso "manual"?
Si te refieres a si hay diferencia alguna entre automático o manual, ninguna
diferencia, a no ser que configures el gc para que no recolecte (con fines de
depuración, claro está)
> ¿Habeis tenido problemas de memoria porque el gc no ha actuado como se
> esperaba?
Pues sí, y es un problema grave de los recolectores de basura, no sólo de
python.
Por ejemplo, es un error común cuando usas varias ventanas, donde los
controles de una ventana están enlazados con los controles de otra, lo que
impide la destrucción de la ventana que enlaza y de todos los controles que
la componen. A medida que se ejecuta la aplicación, la memoria se va llenando
de ventanas y controles que no hay quien los elimine.
Los recolectores de basura están provistos de algoritmos que manejan lo mejor
que pueden las referencias circulares, por lo que casi se puede decir que
cuando surgen problemas hay que buscar primero en el diseño de la aplicación
que se esté haciendo. Aún así, hay una situación irresoluble que ocurre
cuando las referencias circulares se establecen entre objetos que tienen su
propio destructor (método __del__) ya que no se puede saber el orden en que
hay que proceder a destruirlos. El gc simplemente los va acumulando en un
contenedor llamado 'gc.garbage'
Un ejemplo:
>>> class P:
... def __del__(self): print "BORRAR"
...
>>> p=P()
>>> q=P()
>>> p.x=q
>>> q.x=p
>>> del p,q
>>>
No hay mensaje, luego no ha destruído ningún objeto. Veamos dónde paran:
>>> import gc
>>> gc.collect ()
4
>>> gc.garbage
[<__main__.P instance at 0x402f8cac>, <__main__.P instance at 0x402f8ecc>]
>>>
A pesar de que hemos forzado la recolección de basura y que se han obtenido 4
referencias a destruir, seguimos sin ver los mensajes del destructor; las
referencias están guardadas en gc.garbage y los objetos no han sido
destruídos. Si esto fuera una aplicación más o menos compleja (eg: zope) al
final tendríamos un objeto gc.garbage gigantesco malgastando memoria, y lo
peor es que no hay forma de liberar esta memoria de ninguna manera sin salir
de la aplicación. En la documentación habla de hacer 'del gc.garbage[:]' pero
no funciona, y con cualquier cosa que haya probado como 'gc.garbage=[]'.
Por eso, si no queda más remedio y hay que usar el método __del__ en una
clase, la forma de evitarse problemas es emplear referencias débiles:
>>> import weakref
>>> class P:
... def __del__(self):print "BORRAR"
... def wref(self): return weakref.proxy(self)
...
>>> p=P()
>>> q=P()
>>> p.x=q.wref()
>>> q.x=p.wref()
>>>
>>> del p,q
BORRAR
BORRAR
Y mucho cuidado a partir de aquí, ya que las referencias débiles quedan
invalidadas en el momento que se destruyan las referencias fuertes, como
ocurrirá sin darnos cuenta cuando se trabaja con variables locales.
Lectura aconsejable:
"PythonFAQ: ¿Cómo maneja python la memoria?":
http://www.python.org/doc/faq/es/general.html#id43
Más información sobre la lista de distribución Python-es