c# .net garbage-collection suppressfinalize

Lenguaje C#: recolección de basura, SuppressFinalize



.net garbage-collection (5)

¿Qué pasará con los objetos que se llama GC.SuppressFinalize? Entiendo que esto significa que GC no llamará finalizador (destructor) a tales objetos, si es así, ¿cuándo se destruirán realmente estos objetos? De lo contrario, habrá pérdida de memoria, ¿verdad?

Tienes una mala interpretación de para qué sirve la finalización. La finalización es para la limpieza de recursos que no son memoria administrada.

Supongamos que tiene un objeto de tipo de referencia que contiene un campo entero. Ese campo entero simplemente pasa a ser un identificador de un archivo que se obtuvo llamando al código no administrado para abrir el archivo.

Como es posible que algún otro programa quiera acceder a ese archivo, es correcto cerrar el archivo lo antes posible. Pero el tiempo de ejecución de .NET no tiene idea de que este entero tenga un significado especial para el sistema operativo. Es sólo un número entero.

La forma en que resuelve este problema normalmente es que marca el objeto como implementable IDisposable, y luego llama "Eliminar" al objeto tan pronto como haya terminado con él. Su implementación de "Disposición" luego cierra el archivo.

Tenga en cuenta que no hay nada especial aquí. Es solo una convención de que un método que limpia un recurso no administrado se denomina "Eliminar" y un objeto que debe eliminarse implementa IDisposable. La recolección de basura no sabe absolutamente nada de esto.

Entonces, ahora surge el problema: ¿qué pasa si alguien se olvida de llamar a Dispose? ¿El archivo permanece abierto para siempre ? (Claramente, el archivo se cerrará cuando finalice el proceso, pero ¿qué sucede si el proceso se ejecuta durante mucho tiempo?)

Para resolver este problema, utiliza un finalizador. ¿Cómo funciona?

Cuando un objeto está a punto de ser recogido en la basura, el recolector de basura lo comprueba para ver si tiene un finalizador. Si lo hace, entonces, en lugar de que la basura lo recoja, lo coloca en la cola del finalizador. En algún punto no especificado en el futuro, se ejecuta un hilo que examina la cola y llama a un método especial de "Finalizar" en cada objeto. Después de eso, el objeto se elimina de la cola de finalización y se marca como "hey, ya he finalizado". El objeto ahora es una vez más elegible para la recopilación, por lo que el recolector de basura finalmente ejecuta y recopila el objeto sin ponerlo en la cola de finalización.

Claramente, "Finalizar" y "Eliminar" con frecuencia tienen que hacer lo mismo.

Pero ahora surge otro problema. Supongamos que dispones un objeto. Ahora no necesita ser finalizado. La finalización es costosa; mantiene un objeto muerto vivo por mucho más tiempo de lo que necesita ser. Por lo tanto, tradicionalmente cuando uno dispone un objeto, la implementación de Dispose no solo cierra el recurso no administrado, sino que también marca el objeto como "este objeto ya se ha finalizado, no lo finalice de nuevo". De esa manera, engaña al recolector de basura para que no ponga el objeto en la cola de finalización.

Así que vamos a responder a sus preguntas específicas:

¿Qué pasará con los objetos que se llama GC.SuppressFinalize?

Cuando el objeto está muerto, el recolector de basura simplemente reclamará la memoria del objeto sin poner el objeto en la cola del finalizador.

Entiendo que esto significa que GC no llamará finalizador a tales objetos

El GC nunca llama a un finalizador. El hilo finalizador es lo único que llama finalizadores.

¿Cuándo se destruirán realmente estos objetos?

No está claro a qué se refiere con "destruido". Si te refieres a "¿cuándo correrán los finalizadores?" la respuesta es "nunca" porque dijiste que suprimir la finalización. Si quiere decir "¿cuándo se recuperará la memoria en el montón administrado?", La respuesta es "tan pronto como el objeto sea identificado como muerto por el recolector de basura". Eso sucederá antes de lo normal porque la cola del finalizador no mantendrá vivo el objeto.

Estoy leyendo "The C # Language", 4ta edición, habla sobre la recolección de basura de la siguiente manera:

"BILL WAGNER: La siguiente regla es una diferencia importante entre C # y otros entornos administrados.

Antes de la finalización de una aplicación, se llama al destructor para todos sus objetos que aún no se han recolectado como basura, a menos que se haya suprimido dicha limpieza (por ejemplo, mediante una llamada al método de biblioteca GC.SuppressFinalize).

Así que tengo un par de preguntas aquí:

  • Q1. ¿Por qué .net es diferente de otros entornos administrados (supongo que esto indica Java?) Aquí? ¿Algún problema de diseño en particular?

  • Q2. ¿Qué pasará con los objetos que se llama GC.SuppressFinalize ? Entiendo que esto significa que GC no llamará finalizador (destructor) a tales objetos, si es así, ¿cuándo se destruirán realmente estos objetos, de modo que los bits de memoria asignados se devuelvan al montón? De lo contrario habrá pérdida de memoria?


Intento responder Q2:

Si su clase tiene un finalizador (mostrado por ~ ClassName), entonces no será recolectado directamente por la recolección de basura, sino que se colocará en la cola del finalizador que se llamará más adelante. Ahora, cuando finalmente se llama finalizador, generalmente se liberan los recursos no administrados, la clase creada. Si por alguna razón el usuario ya lo hizo, por lo general, al llamar a desechar, el finalizador no necesita limpiar la memoria no administrada, por lo que es necesario llamar a SuppressFinalizer. De lo contrario intentaría liberar los recursos de nuevo. Por lo tanto, la única pérdida de memoria que obtendrá es por los recursos que solicitó en la creación de su clase. El finalizador es solo para liberar todo lo que no está gestionado por el marco.


P1: Sospecho que es porque se preocupa más por lograr un gran rendimiento, a diferencia de Java, que ha hecho algunos sacrificios por la simplicidad.

P2: Dado que los finalizadores ni siquiera están garantizados para ser llamados en primer lugar (incluso si SuppressFinalize no existía), esto solo debe usarse por razones de rendimiento, cuando ya haya eliminado los recursos. De lo contrario, debe utilizar IDisposable para disponer de los recursos.

Finalización! = Destrucción

Los destructores (en el sentido de C ++) no existen en .NET, porque cada objeto, en cierto sentido, tiene un "destructor", llamado recolector de basura. :)
Lo que C # llama "destructores" son en realidad finalizadores. Los finalizadores son para desechar cosas que no sean la memoria asignada por el objeto, como los manejadores de archivos, etc. Por lo tanto, no todos los objetos tenían un finalizador. La memoria siempre es liberada por el GC, por lo que no se produce una pérdida de memoria de esa manera.


Se debe a que una vez se llama a GC.Collect () El objeto con finalizar se moverá a la siguiente generación al menos una vez.

Podemos llamar a GC.SuppressFinalize, y GC sabrá que este objeto está fuera de referencia, entonces podemos eliminar este objeto y compactar el montón.

Por lo general, este implemento con patrón de disposición.


Solo sé la segunda respuesta: SuppressFinalize se llama cuando el objeto ya ha sido destruido, es decir, por IDisposable.Dispose . Así que no debería ser destruido de nuevo.

Esto se debe a que los finalizadores son una limpieza de recursos que se produce en un momento no determinista. IDisposable se agregó para permitir la limpieza de recursos que se produce en un momento específico y predecible.

EDITAR: Al finalizar el proceso, las pérdidas de memoria son irrelevantes. Cuando finaliza un proceso, Windows recopila sus montones, por lo que no importa si la memoria de un objeto se devuelve al montón o no.