En los lenguajes de programación orientados a objetos con recolección de basura , la resurrección de un objeto es cuando un objeto vuelve a la vida durante el proceso de destrucción del objeto , como efecto secundario de la ejecución de un finalizador .
La resurrección de objetos causa una serie de problemas , particularmente porque la posibilidad de la resurrección de objetos, incluso si no ocurre, hace que la recolección de basura sea significativamente más complicada y lenta, y es una de las principales razones por las que los finalizadores no se recomiendan. Los lenguajes tratan la resurrección de objetos de diversas formas, como soluciones a estos problemas. En raras circunstancias, la resurrección de objetos se usa para implementar ciertos patrones de diseño, en particular un grupo de objetos , [1] mientras que en otras circunstancias la resurrección es un error no deseado causado por un error en los finalizadores y, en general, se desaconseja la resurrección. [2]
Proceso
La resurrección de objetos se produce mediante el siguiente proceso. Primero, un objeto se convierte en basura cuando ya no es accesible desde el programa y puede ser recolectado (destruido y desasignado). Luego, durante la destrucción del objeto, antes de que el recolector de basura desasigne el objeto, se puede ejecutar un método de finalizador , que a su vez puede hacer que ese objeto u otro objeto de basura (accesible desde el objeto con un finalizador) sea accesible nuevamente creando referencias a él, como un finalizador puede contener código arbitrario. Si esto sucede, el objeto al que se hace referencia, que no es necesariamente el objeto finalizado, ya no es basura y no se puede desasignar, ya que, de lo contrario, las referencias a él se convertirían en referencias colgantes y causarían errores cuando se usaran, generalmente bloqueo del programa o comportamiento impredecible. En cambio, para mantener la seguridad de la memoria , el objeto vuelve a la vida o resucita.
Para detectar esto, un recolector de basura generalmente hará una recolección en dos fases en presencia de finalizadores: primero finalizará cualquier basura que tenga un finalizador y luego volver a revisar toda la basura (o toda la basura accesible desde los objetos con finalizadores), en caso de que los finalizadores hayan resucitado algo de basura. Esto agrega sobrecarga y retrasa la recuperación de memoria.
Objetos resucitados
Un objeto resucitado puede ser tratado de la misma manera que otros objetos, o puede ser tratado de manera especial. En muchos lenguajes, especialmente C #, Java y Python (de Python 3.4), los objetos solo se finalizan una vez, para evitar la posibilidad de que un objeto resucite repetidamente o incluso sea indestructible; en C #, los objetos con finalizadores de forma predeterminada solo se finalizan una vez, pero se pueden volver a registrar para su finalización. En otros casos, los objetos resucitados se consideran errores, especialmente en Objective-C; o tratado de manera idéntica a otros objetos, especialmente en Python antes de Python 3.4.
Un objeto resucitado a veces se llama objeto zombi ozombi, pero este término se utiliza para varios estados de objetos relacionados con la destrucción de objetos, y el uso depende del idioma y el autor. Sin embargo,un "objeto zombi" tiene un significado especializado enObjective-C, que se detalla a continuación. Los objetos zombies son algo análogos a losprocesos zombies, ya que han sufrido un cambio de estado de terminación y están cerca de la desasignación, pero los detalles son significativamente diferentes.
Variantes
En .NET Framework , en particular C # y VB.NET, "resurrección de objeto" en cambio se refiere al estado de un objeto durante la finalización: el objeto vuelve a la vida (de ser inaccesible), se ejecuta el finalizador y luego se vuelve a siendo inaccesible (y ya no está registrado para una finalización futura). En .NET, los objetos que necesitan finalización no se rastrean objeto por objeto, sino que se almacenan en una "cola" de finalización, [a] por lo que en lugar de una noción de objetos resucitados en el sentido de este artículo, se habla de objetos. "en cola para finalización". Además, los objetos se pueden volver GC.ReRegisterForFinalize
a poner en cola para su finalización a través de , teniendo cuidado de no multiplicar los objetos en la cola. [2]
Mecanismo
Hay dos formas principales en que un objeto puede resucitar a sí mismo oa otro objeto: creando una referencia a sí mismo en un objeto que pueda alcanzar (la basura no es accesible, pero la basura puede hacer referencia a objetos que no son basura), o creando una referencia en el entorno ( variables globales o, en algunos casos, variables estáticas o variables en un cierre ). A continuación se muestran ejemplos de Python de ambos, para un objeto que resucita a sí mismo. También es posible que un objeto resucite a otros objetos si ambos están siendo recolectados en un ciclo de recolección de basura dado, por los mismos mecanismos.
Resucita creando una referencia en un objeto al que puede llegar:
class Clingy : def __init__ ( self , ref = None ) -> None : self . ref = ref def __del__ ( self ): if self . ref : self . ref . ref = self print ( "¡No me dejes!" )a = Clingy ( Clingy ()) # Crea una lista enlazada de 2 elementos, # referenciada por | a | a . ref . ref = a # Crear un ciclo a . ref = None # Borrar la referencia del primer nodo # al segundo hace que la segunda basura a . ref = Ninguno
Resucita creando un referente en el entorno global:
c = Ninguno class Inmortal : def __del__ ( self ): global c c = self print ( "Todavía no estoy muerto" ).c = Inmortal () c = Ninguno # Borrado | c | hace que el objeto sea basura c = Ninguno
En los ejemplos anteriores, en CPython antes de 3.4, estos ejecutarán finalizadores repetidamente y los objetos no serán recolectados como basura, mientras que en CPython 3.4 y posteriores, los finalizadores solo serán llamados una vez y los objetos serán recolectados como basura la segunda vez se vuelven inalcanzables.
Problemas
La resurrección de objetos causa una gran cantidad de problemas.
- Complica la recolección de basura
- La posibilidad de la resurrección de objetos significa que el recolector de basura debe buscar objetos resucitados después de la finalización, incluso si no ocurre realmente, lo que complica y ralentiza la recolección de basura.
- Objetos indestructibles
- En algunas circunstancias, un objeto puede ser indestructible: si un objeto resucita en su propio finalizador (o un grupo de objetos resucita entre sí como resultado de sus finalizadores), y el finalizador siempre se llama al destruir el objeto, entonces el objeto no puede ser destruido y su memoria no puede ser recuperada.
- Resurrección accidental y fugas
- En tercer lugar, la resurrección del objeto puede ser involuntaria y el objeto resultante puede ser basura semántica, por lo que nunca se recopila realmente, lo que provoca una pérdida de memoria lógica .
- Estado inconsistente y reinicialización
- Un objeto resucitado puede estar en un estado inconsistente, o violar invariantes de clase , debido a que el finalizador se ejecutó y causó un estado irregular. Por lo tanto, los objetos resucitados generalmente deben reinicializarse manualmente. [1]
- Finalización única o re-finalización
- En algunos lenguajes (como Java y Python 3.4+) se garantiza que la finalización ocurrirá exactamente una vez por objeto, por lo que los objetos resucitados no tendrán sus finalizadores llamados; por lo tanto, los objetos resucitados deben ejecutar cualquier código de limpieza necesario fuera del finalizador. En algunos otros lenguajes, el programador puede forzar la finalización repetidamente; en particular, C # tiene
GC.ReRegisterForFinalize
. [1]
Soluciones
Los lenguajes han adoptado varios métodos diferentes para hacer frente a la resurrección de objetos, más comúnmente mediante la recolección de basura en dos fases en presencia de finalizadores, para evitar referencias colgantes; y finalizando los objetos una sola vez, en particular marcando los objetos como finalizados (mediante una bandera), para garantizar que los objetos puedan ser destruidos.
Java no liberará el objeto hasta que haya demostrado que el objeto es una vez más inalcanzable, pero no ejecutará el finalizador más de una vez. [3]
En Python, antes de Python 3.4, la implementación estándar de CPython trataría los objetos resucitados de manera idéntica a otros objetos (que nunca se habían finalizado), haciendo posibles los objetos indestructibles. [4] Además, no haría ciclos de recolección de basura que contengan un objeto con un finalizador, para evitar posibles problemas con la resurrección del objeto. A partir de Python 3.4, el comportamiento es básicamente el mismo que el de Java: [b] los objetos solo se finalizan una vez (se marcan como "ya finalizados"), la recolección de basura de los ciclos se realiza en dos fases, con la segunda fase comprobando los objetos resucitados. [5] [6]
Objective-C 2.0 pondrá los objetos resucitados en un estado "zombi", donde registrarán todos los mensajes que se les envíen, pero no harán nada más. [7] Véase también Recuento automático de referencias: puesta a cero de referencias débiles para el manejo de referencias débiles .
En .NET Framework, en particular C # y VB.NET, la finalización del objeto se determina mediante una "cola" de finalización, [a] que se comprueba durante la destrucción del objeto. Los objetos con un finalizador se colocan en esta cola en el momento de la creación y se retiran de la cola cuando se llama al finalizador, pero se pueden retirar manualmente (antes de la finalización) SuppressFinalize
o volver a ponerlos en cola ReRegisterForFinalize
. Por lo tanto, de forma predeterminada, los objetos con finalizadores se finalizan como máximo una vez, pero esta finalización se puede suprimir, o los objetos se pueden finalizar varias veces si se resucitan (se vuelven accesibles de nuevo) y luego se vuelven a poner en cola para su finalización. Además, las referencias débiles por defecto no rastrean la resurrección, lo que significa que una referencia débil no se actualiza si se resucita un objeto; estas se denominan referencias débiles cortas y las referencias débiles que rastrean la resurrección se denominan referencias débiles largas . [8]
Aplicaciones
La resurrección de objetos es útil para manejar un grupo de objetos de uso común, pero oscurece el código y lo hace más confuso. [3] Debe usarse solo para objetos que puedan usarse con frecuencia y donde su construcción / destrucción requiera mucho tiempo. Un ejemplo podría ser una matriz de números aleatorios, donde una gran cantidad de ellos se crea y destruye en poco tiempo, pero donde en realidad solo se usa una pequeña cantidad al mismo tiempo. Con la resurrección de objetos, una técnica de agrupación reduciría la sobrecarga innecesaria de creación y destrucción. Aquí, un administrador de grupo obtendría la información de su pila de objetos en forma de referencia al objeto, si actualmente se va a destruir. El administrador de la piscina conservará el objeto para reutilizarlo más tarde. [9]
Ver también
Notas
- ^ a b Esto no es estrictamente una cola, ya que los elementos se pueden eliminar del medio mediante
GC.SuppressFinalization
. - ^ CPython usa recuentos de referencia para basura no cíclica, con un detector de ciclo separado, mientras que la mayoría de las implementaciones de Java usan un recolector de basura de rastreo.
Referencias
- ↑ a b c Goldshtein, Zurbalev y Flatow 2012 , p. 129 .
- ^ a b Richter 2000 .
- ^ a b "¿Qué es la resurrección (en la recolección de basura)?" . http://www.xyzws.com/ : XYZWS . Consultado el 1 de agosto de 2011 .
Un objeto que ha sido elegible para la recolección de basura puede dejar de ser elegible y volver a su vida normal. Dentro de un método finalize (), puede asignar esto a una variable de referencia y evitar la colección de ese objeto, un acto que muchos desarrolladores llaman resurrección. / La JVM nunca llama al método finalize () más de una vez para un objeto determinado. La JVM no invocará el método finalize () nuevamente después de la resurrección (ya que el método finalize () ya se ejecutó para ese objeto).
- ^ La respuesta de Tim Peters a " ¿Cuántas veces se puede llamar a` __del__` por objeto en Python? "
- ^ Novedades de Python 3.4 , PEP 442: Finalización segura de objetos
- ^ Pitrou, Antoine (2013). "PEP 442 - Finalización segura de objetos" .
- ^ Implementación de un método de finalización
- ^ Goldshtein, Zurbalev y Flatow 2012 , p. 131 .
- ^ "Resurrección de objetos" (PDF) . http://www.hesab.net/ : Hesab.net . Consultado el 1 de agosto de 2011 .
La resurrección de objetos es una técnica avanzada que probablemente sea útil solo en escenarios inusuales, como cuando está implementando un grupo de objetos cuya creación y destrucción requiere mucho tiempo. ... La aplicación de demostración ObjectPool muestra que un administrador de grupo de objetos puede mejorar el rendimiento cuando muchos objetos se crean y destruyen con frecuencia. Suponga que tiene una clase RandomArray, que encapsula una matriz de números aleatorios. El programa principal crea y destruye miles de objetos RandomArray, aunque solo unos pocos objetos están vivos en un momento dado. Debido a que la clase crea la matriz aleatoria en su método constructor (una operación que consume mucho tiempo), esta situación es ideal para una técnica de agrupación. ... El punto crucial en la técnica de agrupación es que la clase PoolManager contiene una referencia a los objetos no utilizados en la agrupación (en el objeto PooledObjects Stack), pero no a los objetos que utiliza el programa principal. De hecho, estos últimos objetos se mantienen vivos solo mediante referencias en el programa principal. Cuando el programa principal establece un objeto RandomArray en Nothing (o lo deja fuera de alcance) y ocurre una recolección de basura, el recolector de basura invoca el método Finalize del objeto. Por lo tanto, el código dentro del método Finalize de RandomArray tiene la oportunidad de resucitar almacenando una referencia a sí mismo en la estructura PooledObjects de PoolManager. Por tanto, cuando se vuelve a llamar a la función NewRandomArray, el objeto PoolManager puede devolver un objeto agrupado al cliente sin pasar por el lento proceso de creación de uno nuevo.
- Goldshtein, Sasha; Zurbalev, Dima; Flatow, Ido (2012). Rendimiento Pro .NET: Optimice sus aplicaciones C # . Presione. ISBN 978-1-4302-4458-5.
- Richter, Jeffrey (noviembre de 2000). "Recolección de basura: gestión automática de memoria en Microsoft .NET Framework" . Revista MSDN .