2012-06-09 23 views
26

Estoy tratando de entender cómo el recolector de basura de Python detecta referencias circulares. Cuando miro la documentación, todo lo que veo es una declaración de que se han detectado referencias circulares, excepto cuando los objetos involucrados tienen un método __del__.¿Cómo detecta Garbage Collector de Python las referencias circulares?

Si esto sucede, mi comprensión (posiblemente defectuosa) es que el módulo gc actúa como una protección contra fallas (supongo) recorriendo toda la memoria asignada y liberando cualquier bloque inalcanzable.

¿Cómo detecta Python & referencias de memoria circular libre antes de hacer uso del módulo gc?

+0

" ..pero está garantizada _No_ para recoger la basura que contiene referencias circulares " Diga [docs] (http://docs.python.org/reference/datamodel.html). –

+0

¿Puede vincular la página de la documentación a la que se refiere? –

+0

Aquí está la página que estaba leyendo: http://docs.python.org/extending/extending.html#reference-counts – user1245262

Respuesta

5

Creo que he encontrado la respuesta que estoy buscando en algunos enlaces proporcionados por @SvenMarnich en los comentarios a la pregunta original:

objetos contenedores son objetos de Python que pueden contener referencias a otros objetos de Python. Listas, Clases, Tuplas, etc. son objetos contenedores; Enteros, cadenas etc. no lo son. Por lo tanto, solo los objetos contenedores están en riesgo de estar en una referencia circular.

Cada objeto Python tiene un campo - * gc_ref *, que (creo) está establecido en NULL para objetos que no son contenedores. Para los objetos contenedores se establece igual al número de objetos sin contenedor que lo hacen referencia

Cualquier objeto contenedor con un * gc_ref * cuenta mayor que 1 (? Hubiera pensado 0, pero ¿está de acuerdo por ahora?) tiene referencias que no son objetos contenedores. De modo que son accesibles y se eliminan de la consideración de islas de memoria inalcanzables.

Cualquier objeto contenedor al que pueda acceder un objeto conocido (es decir, los que acabamos de reconocer que tienen un recuento * gc_ref * mayor que 1) tampoco necesita ser liberado.

El resto de los objetos del contenedor no son alcanzables (excepto unos de otros) y deben liberarse.

http://www.arctrix.com/nas/python/gc/ es un enlace que proporciona una explicación más completa http://hg.python.org/cpython/file/2059910e7d76/Modules/gcmodule.c es un enlace al código fuente, que tiene los comentarios que explican aún más los pensamientos detrás de la detección referencia circular

+0

Supongo que mi preferencia por esta respuesta es idiosincrásica, pero los enlaces que proporcionó @SvenMarnich me dieron una respuesta en términos que podía entender y explicar fácilmente a otra persona. La explicación dada por David Wolever también fue bastante buena y si quiero modificar/modificar o recoger objetos que creo e implemento en C, será muy útil. – user1245262

22

¿Cómo detecta Python & referencias de memoria circular libre antes de hacer uso del módulo gc?

No lo es. El gc existe solo a detectar y referencias circulares libres. Las referencias no circulares se manejan a través de refcounting.

Ahora, para ver cómo gc determina el conjunto de objetos referenciados por cualquier objeto dado, echar un vistazo a la función gc_get_references en Modules/gcmodule.c. El bit relevante es:

// Where `obj` is the object who's references we want to find 
traverseproc traverse; 
if (! PyObject_IS_GC(obj)) 
    continue; 
traverse = Py_TYPE(obj)->tp_traverse; 
if (! traverse) 
    continue; 
if (traverse(obj, (visitproc)referentsvisit, result)) { 
    Py_DECREF(result); 
    return NULL; 
} 

La función principal aquí es tp_traverse. Cada tipo de nivel C define una función tp_traverse (o en el caso de objetos que no tienen ninguna referencia, como str, lo establece en NULL). Un ejemplo de tp_traverse es list_traverse, la función de recorrido para list:

static int 
list_traverse(PyListObject *o, visitproc visit, void *arg) 
{ 
    Py_ssize_t i; 

    for (i = Py_SIZE(o); --i >= 0;) 
     Py_VISIT(o->ob_item[i]); 
    return 0; 
} 

lo que veo es una declaración que se detectan referencias circulares, excepto cuando los objetos involucrados tienen un método __del__().

Estás en lo correcto - detector de ciclo de Python puede detectar y recoger los ciclos menos que contienen objetos con un método __del__, ya que no hay manera para que el intérprete para eliminar de forma segura estos objetos (para obtener una intuición sobre por qué esto es decir, imagine que tiene dos objetos con métodos __del__ que se referencian entre sí. ¿En qué orden deberían liberarse?).

Cuando los objetos con un método __del__ están involucrados en un ciclo, el recolector de basura los pegará en una lista separada (accesible a través de gc.garbage) para que el programador pueda "tratarlos" manualmente.

+1

En los documentos, vi la siguiente afirmación: "El detector de ciclos puede detectar ciclos de basura y puede reclamarlos siempre que no se implementen finalizadores en los métodos de Python (__del __()). Cuando hay tales finalizadores, el detector expone los ciclos a través del módulo gc (específicamente, la variable basura en ese módulo). "http://docs.python.org/extendiendo/extendiendo.html#reconocimiento-include ... interpreté esto como que significaba que gc era un método a prueba de fallas/más lento. ¿Estaba malinterpretando los documentos (podría haberlo sido fácilmente)? – user1245262

+2

@ user1245262, el problema '__del__' no está realmente relacionado con la búsqueda de la basura. Python encuentra que tales objetos son basura y los pegan en la lista 'gc.garbage' La única razón por la cual dichos objetos no se eliminan es que python no puede decir cuál es el orden seguro para eliminarlos. –

+1

Ah, lo siento, olvidé abordar esa pregunta. Creo que eso significa: "cuando no hay objetos con métodos' __del__', el detector de ciclos puede reclamar todo. Sin embargo, dado que el detector de ciclos no puede recoger objetos de forma segura con los métodos '__del__', existen ciclos que involucran a estos objetos. a través del módulo 'gc', permitiendo que el programador los limpie manualmente. –

4

¿Cómo detecta Python & referencias de memoria circular libre antes de hacer uso del módulo gc?

recolector de basura de Python (no en realidad el módulo de gc, que es sólo la interfaz de Python para el recolector de basura) hace esto. Por lo tanto, Python no detecta referencias de memoria circulares libres antes de utilizar el recolector de elementos no utilizados.

Python normalmente libera la mayoría de los objetos tan pronto como su recuento de referencia llega a cero. (Digo "la mayoría" porque nunca se libera, por ejemplo, enteros pequeños o cadenas internas). En el caso de las referencias circulares, esto nunca ocurre, por lo que el recolector de basura recorre periódicamente la memoria y libera objetos referenciados circularmente.

Esto es todo específico de CPython, por supuesto. Otras implementaciones de Python tienen una administración de memoria diferente (Jython = Java VM, IronPython = Microsoft .NET CLR).

+0

No es del todo exacto. Python no recorre la memoria y libera objetos inalcanzables. Recorre la memoria y detecta ciclos de referencias y los libera. –

+0

¿"recorre la memoria"? Pensé que caminó listas de referencias? (¿La "memoria de andar" tiene una definición técnica con la que no estoy familiarizado?) –

+0

El módulo 'gc' * es * el recolector de basura cíclico. No es solo la interfaz de Python, es la implementación. –

Cuestiones relacionadas