2010-04-05 16 views
6

Estoy tratando de entender las partes internas del recolector de basura CPython, específicamente cuando se llama al destructor. Hasta el momento, el comportamiento es intuitivo, pero el siguiente caso me provoca:¿Por qué se llama al destructor cuando el recolector de basura CPython está deshabilitado?

  1. Deshabilitar el GC.
  2. Crea un objeto y luego quita una referencia al mismo.
  3. El objeto se destruye y se llama al método _____del_____.

Pensé que esto solo ocurriría si el recolector de basura estuviera habilitado. ¿Alguien puede explicar por qué sucede esto? ¿Hay alguna manera de diferir llamando al destructor?

import gc 
import unittest 

_destroyed = False 

class MyClass(object): 

    def __del__(self): 
     global _destroyed 
     _destroyed = True 

class GarbageCollectionTest(unittest.TestCase): 

    def testExplicitGarbageCollection(self): 
     gc.disable() 
     ref = MyClass() 
     ref = None 
     # The next test fails. 
     # The object is automatically destroyed even with the collector turned off. 
     self.assertFalse(_destroyed) 
     gc.collect() 
     self.assertTrue(_destroyed) 

if __name__=='__main__': 
    unittest.main() 

responsabilidad: este código no es para la producción - Ya he señalado que esto es muy aplicación específica y no funciona en Jython.

Respuesta

9

Python tiene tanto referencia contando recolección de basura y cíclica recolección de basura, y es este último el que los controles gc módulo. El recuento de referencias no se puede deshabilitar y, por lo tanto, aún sucede cuando el recolector de basura cíclico está apagado.

Dado que no quedan referencias a su objeto después de ref = None, se llama a su método __del__ como resultado de que su recuento de referencia va a cero.

Hay una pista en the documentation: "Desde el colector suplementos el recuento de referencias que ya se utiliza en Python ..." (el subrayado es mío).

Puede detener la primera afirmación de disparar, haciendo que el objeto se refiere a sí mismo, de modo que su contador de referencia no va a cero, por ejemplo, dándole a este constructor:

def __init__(self): 
    self.myself = self 

Pero si haz eso, la segunda afirmación se disparará. Esto se debe a que los ciclos de basura con los métodos __del__ no se recopilan: consulte la documentación para gc.garbage.

4

Dependiendo de su definición de recolector de basura, CPython tiene dos recolectores de basura, uno de recuento de referencia y el otro.
El contador de referencia siempre funciona y no se puede apagar, ya que es bastante rápido y liviano y no afecta significativamente el tiempo de ejecución del sistema.
El otro (algunas variaciones de marca y barrido, creo) se ejecuta cada cierto tiempo y se puede deshabilitar. Esto se debe a que requiere la pausa del intérprete mientras se está ejecutando, y esto puede suceder en el momento incorrecto y consumir bastante tiempo de CPU.
Esta capacidad para deshabilitarlo está ahí para aquellos momentos en los que espera hacer algo que es crítico en el tiempo, y la falta de este GC no le causará ningún problema.

+0

¿Está documentada esta implementación de "dos recolectores de basura" en alguna parte? – Frederik

+0

Eche un vistazo a la respuesta de Alex Martelli y sus enlaces asociados. Probablemente sea mejor que cualquier otra cosa que se me ocurra. –

4

Los documentos explican cómo here lo que se llama "el recolector de basura opcional" es en realidad un colector de basura cíclica (el tipo que el recuento de referencias no cogería).recuento de referencias se explica here, con un guiño a su interacción con la GC cíclica:

Mientras Python usa la tradicional aplicación de conteo referencia, también ofrece un detector de ciclo que funciona para detectar ciclos de referencia. Este permite que las aplicaciones no se preocupen por creando referencias circulares directas o indirectas ; estos son los puntos débiles de la recolección de basura implementada usando solo recuento de referencias. Los ciclos de referencia consisten en objetos que contienen referencias (posiblemente indirectas) , de modo que cada objeto en el ciclo tiene un recuento de referencia que es distinto de cero. Típicos referencia implementaciones de conteo no son capaces para recuperar la memoria que pertenece a cualquier objetos en un ciclo de referencia, o referencia de los objetos en el ciclo , a pesar de que no hay referencias adicionales al ciclo sí mismo.

Cuestiones relacionadas