c# .net garbage-collection idisposable suppressfinalize

c# - ¿Cuál es el propósito de GC.SuppressFinalize(this) en el método Dispose()?



.net garbage-collection (5)

Tengo el siguiente código:

public void Dispose() { if (_instance != null) { _instance = null; // Call GC.SupressFinalize to take this object off the finalization // queue and prevent finalization code for this object from // executing a second time. GC.SuppressFinalize(this); } }

Aunque hay un comentario que explica el propósito de esa llamada relacionada con GC, todavía no entiendo por qué está allí.

¿El objeto no está destinado a la recolección de basura una vez que todas las instancias dejan de existir, como, cuando se usa en el using bloque?

¿Cuál es el caso de uso en el que esto jugaría un papel importante?


Al implementar el patrón de disposición, también puede agregar un finalizador a su clase que llame a Dispose() . Esto es para asegurarse de que siempre se llame a Dispose() , incluso si un cliente se olvida de llamarlo.

Para evitar que el método de disposición se ejecute dos veces (en caso de que el objeto ya se haya eliminado), agregue GC.SuppressFinalize(this); . La documentación proporciona una sample :

class MyResource : IDisposable { [...] // This destructor will run only if the Dispose method // does not get called. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. component.Dispose(); } // Call the appropriate methods to clean up // unmanaged resources here. resource.Cleanup() } disposed = true; } }


Desde MSDN: sample :

Este método establece un bit en el encabezado del objeto, que el sistema verifica cuando llama a los finalizadores. El parámetro obj debe ser el llamador de este método.

Los objetos que implementan la interfaz IDisposable pueden llamar a este método desde el método IDisposable .. ::. Dispose para evitar que el recolector de basura llame a Object .. ::. Finalize en un objeto que no lo requiera.

Normalmente, usaría esto si su objeto no hace referencia a otros objetos, solo tipos discretos, o si ya ha restablecido cualquier referencia de objeto a NULL.


Los objetos que pueden finalizarse sobreviven a la primera ejecución de GC.

Normalmente, cuando el GC detecta que un objeto es inalcanzable, lo recupera. Si el objeto es finalizable, entonces el GC no lo reclama; en su lugar, considera que es alcanzable (y todos los objetos a los que hace referencia este objeto, y así sucesivamente), y lo programa para su finalización. El objeto se reclamará solo cuando se encuentre nuevamente como inalcanzable en algún momento después de que se haya finalizado.

Esto significa que un objeto finalizable incurre en un costo adicional: el objeto debe mantenerse en la memoria durante más tiempo. De ahí la llamada que se ve: vale la pena suprimir la finalización cuando no es necesaria. Aquí, el objeto utiliza la finalización para garantizar que siempre se "elimine" en algún momento. Cuando se dispone de manera explícita, no necesita ser finalizado nunca más.


Si su tipo implementa un finalizador ( ~MyType() { } ), evita que el recolector de basura lo ejecute. Se utiliza cuando el finalizador se ocupa de los tipos no administrados, pero el usuario ya ha llamado a Dispose() (ya sea explícitamente o mediante un bloque using() { } ), liberando esos tipos no administrados.


Recolección de basura : GC recupera la memoria utilizada por el objeto cuando ya no se hace referencia al objeto.

Dispose : un método de la interfaz IDisposable que es liberar todos los recursos administrados y no administrados cuando el programador lo llama (ya sea directa o indirectamente a través de un bloque de uso).

Finalizer : un método para liberar todos los recursos no administrados. Llamado por el GC antes de reclamar la memoria.

Recurso gestionado : cualquier clase .NET que implemente la interfaz IDisposable , como Streams y DbConnections.

Recurso no administrado : el relleno envuelto en las clases de Recurso Gestionado. Los manejadores de Windows son los ejemplos más triviales.

Ahora para responder a tu pregunta:

GC mantiene una lista (cola de finalización) de todos los objetos cuya clase declara un finalizador (~ ClassName en C #). Los objetos se ponen en esta cola en la creación. GC se ejecuta periódicamente para verificar si hay objetos inaccesibles desde el programa. Luego verifica si se hace referencia a alguno de los objetos inaccesibles de la cola de finalización, y los coloca en otra cola llamada la cola Freacheable, mientras que el resto se reclama. Se utiliza un hilo separado para ejecutar los métodos Finalize de objetos en la cola Freacheable.

La próxima vez que se ejecute GC, se encontrará que algunos de los objetos que se encontraban anteriormente en la cola de Freacheable ya están finalizados, por lo que están listos para su reclamación. Tenga en cuenta que GC necesita al menos dos ciclos (o mucho más si hay mucha Finalización por hacer) para deshacerse de un objeto con un Finalizer, que incurre en algunas penalizaciones de rendimiento.

El método SuppressFinalize simplemente establece una bandera en el encabezado del objeto que indica que el Finalizer no tiene que ejecutarse. De esta manera, GC puede recuperar la memoria del objeto de inmediato. Según la definición anterior, el método de Dispose hace lo mismo que el Finalizer (y más), por lo que si se ejecuta, la Finalización ya no es necesaria. Usando el método SuppressFinalize , puede guardar algo de trabajo para el GC notificándolo sobre este hecho. Además, ahora no tiene que implementar comprobaciones en el Finalizer para evitar la doble liberación. El único problema con Dispose es que no se garantiza que se ejecute, ya que es responsabilidad del programador llamarlo, por eso a veces debemos molestarnos con los finalizadores.

Dicho esto, es muy poco frecuente que necesite escribir un Finalizer, ya que para la mayoría de los recursos no administrados habituales ya existe un contenedor administrado, y los recursos administrados deben liberarse llamando a sus métodos de Dispose desde su propio Dispose Método, y desde allí sólo! En los finalizadores nunca debes llamar a un método de Disposición.

Lectura adicional :