tag remarks example c# winforms dispose

c# - remarks - ¿Qué sucede si no llamo Dispose en el objeto de lápiz?



remarks c# (10)

Con material gráfico, puede ser muy malo.

Abra el Administrador de tareas de Windows. Haga clic en "elegir columnas" y elija la columna llamada "Objetos GDI".

Si no dispone de ciertos objetos gráficos, este número seguirá aumentando y subiendo.

En versiones anteriores de Windows, esto puede bloquear toda la aplicación (el límite era 10000 por lo que recuerdo), pero no estoy seguro de Vista / 7, pero sigue siendo una mala cosa.

¿Qué sucede si no llamo Dispose en el objeto de pen en este fragmento de código?

private void panel_Paint(object sender, PaintEventArgs e) { var pen = Pen(Color.White, 1); //Do some drawing }


Depende si implementa el finalizador y llama a Dispose en su método de finalización. Si es así, se liberará handle en GC.

si no, el identificador se mantendrá hasta que finalice el proceso.


El GC será recolectado por el GC en algún momento indeterminado en el futuro, ya sea que llame o no a Dispose .

Sin embargo, cualquier recurso no administrado que tenga el bolígrafo (por ejemplo, un identificador GDI +) no será limpiado por el GC. El GC solo limpia los recursos administrados. Calling Pen.Dispose permite asegurarse de que estos recursos no administrados se limpien oportunamente y de que no esté perdiendo recursos.

Ahora, si el Pen tiene un finalizador y ese finalizador limpia los recursos no administrados, dichos recursos no administrados se limpiarán cuando el Pen se recolecte basura. Pero el punto es que:

  1. Debe llamar a Dispose explícitamente para que libere sus recursos no administrados, y
  2. No debería preocuparse por los detalles de implementación de si hay un finalizador y limpia los recursos no administrados.

Pen implementa IDisposable . IDisposable es para eliminar recursos no administrados. Este es el patrón en .NET.

Para comentarios previos sobre este tema, vea esta answer .


El identificador subyacente del lápiz GDI + no se liberará hasta un tiempo indeterminado en el futuro, es decir, cuando el objeto Pen sea basura y se llame al finalizador del objeto. Esto podría no ser hasta que el proceso finalice, o podría ser anterior, pero el punto es que no es determinista. Calling Dispose le permite realizar una limpieza determinista y es muy recomendable.


En el fondo de mi mente, la primera idea que salió a la superficie es que este objeto se eliminará tan pronto como el método termine la ejecución, ¡no sé de dónde saqué esta información !, ¿está bien?


La cantidad total de memoria .Net en uso es la parte .Net + todos los datos ''externos'' en uso. Los objetos del sistema operativo, los archivos abiertos, la base de datos y las conexiones de red toman algunos recursos que no son puramente objetos .Net.

Graphics usa bolígrafos y otros objetos que en realidad son objetos del sistema operativo que son "bastante" caros de mantener. (Puede cambiar su Pen por un archivo de mapa de bits 1000x1000). Estos objetos del sistema operativo solo se eliminan de la memoria del sistema operativo una vez que llama a una función de limpieza específica. Las funciones Disposición de lápiz y mapa de bits lo hacen por usted inmediatamente cuando los llama.

Si no llamas a Dispose, el recolector de basura vendrá a limpiarlos ''en algún lugar en el futuro *''. (En realidad llamará al código destructor / finalize que probablemente llama Dispose ())

* en una máquina con memoria infinita (o más de 1GB) en algún lugar en el futuro puede ser muy lejano en el futuro. En una máquina que no hace nada, puede tardar más de 30 minutos en limpiar ese enorme mapa de bits o una pluma muy pequeña.


Mantendrá los recursos hasta que el recolector de basura lo limpie


Si realmente quieres saber qué tan malo es cuando no llamas a Dispose en objetos gráficos, puedes usar el CLR Profiler, que está disponible gratis para descargarlo here. En la carpeta de instalación (por defecto es C: / CLRProfiler) está CLRProfiler.doc que tiene un buen ejemplo de lo que ocurre cuando no se llama a Dispose en un objeto Brush. Es muy esclarecedor También es posible que desee leer sobre el uso de IDisposable here y here .


Un par de correcciones deben hacerse aquí:

En cuanto a la respuesta de Phil Devaney:

"... Calling Dispose le permite realizar una limpieza determinista y es muy recomendable".

En realidad, llamar a Dispose () no determina de manera determinista una colección de GC en .NET, es decir, NO activa un GC inmediatamente solo porque llamó a Dispose (). Solo indirectamente indica al GC que el objeto se puede limpiar durante el siguiente GC (para la generación en la que el objeto vive). En otras palabras, si el objeto vive en Gen 1, entonces no se eliminará hasta que tenga lugar una recolección de Gen 1. Una de las únicas formas (aunque no la única) que puede realizar de forma programática y determinista que el GC realice una recopilación es llamando a GC.Collect (). Sin embargo, no se recomienda hacerlo porque el GC "se sintoniza" durante el tiempo de ejecución recopilando métricas sobre las asignaciones de memoria durante el tiempo de ejecución de su aplicación. Al llamar a GC.Collect () vuelca esas métricas y hace que el GC inicie su "ajuste" de nuevo.

En cuanto a la respuesta:

IDisposable es para eliminar recursos no administrados . Este es el patrón en .NET.

Esto es incompleto Como el GC no es determinista, el Patrón de eliminación, ( Cómo implementar correctamente el patrón Dispose ), está disponible para que pueda liberar los recursos que está utilizando, administrados o no. No tiene nada que ver con qué tipo de recursos estás liberando. La necesidad de implementar un Finalizador tiene que ver con qué tipo de recursos está utilizando, es decir, ÚNICAMENTE implementar uno si tiene recursos no finalizables (es decir, nativos). Quizás estás confundiendo a los dos. Por cierto, debe evitar la implementación de un Finalizer utilizando la clase SafeHandle que contiene los recursos nativos que se calculan mediante P / Invoke o COM Interop. Si termina implementando un Finalizador, siempre debe implementar el Patrón de eliminación.

Una nota crítica que no he visto a nadie mencionar es que si se crea un objeto desechable y tiene un Finalizador (y uno nunca sabe realmente si lo hacen, y ciertamente no se debe hacer ninguna suposición al respecto), entonces lo hará. ser enviado directamente a la cola de finalización y vivir por lo menos una colección extra de GC .

Si GC.SuppressFinalize () no se llama en última instancia, se llamará al finalizador del objeto en el siguiente GC. Tenga en cuenta que una implementación adecuada del patrón Dispose debe llamar a GC.SuppressFinalize (). Por lo tanto, si llama a Dispose () en el objeto y lo ha implementado correctamente, evitará la ejecución del Finalizer. Si no llama a Dispose () en un objeto que tiene un finalizador, el objeto tendrá su Finalizer ejecutado por el GC en la próxima colección. ¿Por qué es esto malo? El subproceso Finalizer en el CLR hasta e incluyendo .NET 4.6 es de subproceso único. Imagine lo que sucede si aumenta la carga de este hilo: el rendimiento de su aplicación se dirige a su ubicación.

Llamar a Dispose en un objeto proporciona lo siguiente:

  1. reducir la tensión en el GC para el proceso;
  2. reducir la presión de la memoria de la aplicación;
  3. reducir la posibilidad de una OutOfMemoryException (OOM) si LOH (Large Object Heap) se fragmenta y el objeto está en LOH;
  4. Mantenga el objeto fuera de las colas Finalizable y f-reach si tiene un Finalizer;
  5. Asegúrese de que sus recursos (administrados y no administrados) se limpien de manera determinista.

Editar : Me acabo de dar cuenta de que la documentación de MSDN "con todos los conocimientos y siempre correcta" sobre IDisposable (sarcasmo extremo aquí) en realidad dice

El uso principal de esta interfaz es liberar recursos no administrados

Como cualquiera debería saber, MSDN está lejos de ser correcto, nunca menciona o muestra las "mejores prácticas", a veces proporciona ejemplos que no compilan, etc. Es desafortunado que esto esté documentado en esas palabras. Sin embargo, sé lo que estaban tratando de decir: en un mundo perfecto, el GC limpiará todos los recursos administrados para usted (qué idealista); no lo hará, sin embargo, limpie los recursos no administrados . Esto es absolutamente cierto. Dicho esto, la vida no es perfecta ni tampoco ninguna aplicación. El GC solo limpiará recursos que no tengan referencias rooteadas. Esto es principalmente donde radica el problema.

Entre aproximadamente 15-20 formas diferentes en que .NET puede "filtrar" (o no) la memoria, la que probablemente te morderá si no llamas a Dispose () es la falla al anular el registro / desenganche / desenredo / desprender el evento manipuladores / delegados. Si crea un objeto que tiene delegados conectados a él y no llama a Dispose () (y no separa los delegados usted mismo), el GC seguirá viendo el objeto como si tuviera referencias rooteadas, es decir, los delegados. Por lo tanto, el GC nunca lo recogerá.

@ joren''s comment / question below (mi respuesta es demasiado larga para un comentario):

Tengo una publicación en el blog sobre el patrón Dispose que recomiendo usar - ( Cómo implementar correctamente el patrón Dispose ). Hay momentos en los que debe anular las referencias y nunca está de más hacerlo. En realidad, hacerlo hace algo antes de que se ejecute GC: elimina la referencia rooteada de ese objeto. El GC luego escanea su colección de referencias rooteadas y recopila aquellas que no tienen una referencia rooteada. Piensa en este ejemplo cuando es bueno hacerlo: tienes una instancia del tipo "Clase A", llamémosla "X". X contiene un objeto de tipo "ClassB" - vamos a llamarlo ''Y''. Y implementa IDisposable, por lo tanto, X debería hacer lo mismo para deshacerse de Y. Supongamos que X está en la Generación 2 o LOH e Y está en la Generación 0 o 1. Cuando se usa Dispose () en X y esa implementación anula el referencia a Y, la referencia arraigada a Y se elimina inmediatamente. Si ocurre un GC para Gen 0 o Gen 1, la memoria / recursos para Y se limpian, pero la memoria / recursos para X no es porque X vive en Gen 2 o LOH.


el recolector de basura lo recogerá de todos modos PERO es importante CUANDO: si no llama a deshacerse de un objeto que no usa, vivirá más tiempo en la memoria y se promocionará a generaciones más altas, lo que significa que su recolección tiene un costo mayor.