.net - pattern - protected override void dispose(bool disposing)
¿Es importante disponer de SolidBrush y Pen? (6)
Hace poco me encontré con este control VerticalLabel en CodeProject .
Observo que el método OnPaint crea pero no desecha los objetos Pen y SolidBrush.
¿Importa esto, y si es así, cómo puedo demostrar cualquier problema que pueda causar?
EDITAR
Esto no es una pregunta sobre el patrón IDisponible en general. Entiendo que las personas que llaman normalmente deben llamar a Dispose en cualquier clase que implemente IDisposable.
Lo que quiero saber es qué problemas (si los hay) pueden esperarse cuando el objeto GDI + no se elimina como en el ejemplo anterior. Está claro que, en el ejemplo vinculado, se puede llamar a OnPaint muchas veces antes de que el recolector de basura entre en acción, por lo que existe la posibilidad de quedarse sin manijas.
Sin embargo, sospecho que GDI + reutiliza internamente los mangos en algunas circunstancias (por ejemplo, si usa un lápiz de un color específico de la clase Pens, se almacena en caché y se reutiliza).
Lo que estoy tratando de entender es si un código como ese en el ejemplo vinculado podrá salirse con la negligencia de llamar a Dispose.
Y si no, para ver una muestra que demostró los problemas que puede causar.
Debo agregar que muy a menudo ( incluyendo la documentación de OnPaint en MSDN ) he visto ejemplos de código de WinForms que no pueden desechar los objetos GDI +.
Además, diría que es preferible utilizar las colecciones listas: System.Drawing.Pens. y System.Drawing.Brushes. Si no los personalizas de una manera especial.
FWIW, en GDI hay objetos "stock". Cuando creas un objeto de stock, no tienes que eliminarlo porque es "propiedad" del sistema operativo.
Probablemente ya sepa acerca de los objetos de stock, pero aquí hay un link que detalla algunos detalles.
No sé si hay objetos "stock" similares en GDI +. Acabo de buscar brevemente y no he encontrado ninguna referencia a tales.
Como prueba escribí un pequeño programa de WinForms con una devolución de llamada del temporizador (configurado para activarse cada 10 milisegundos) como esto:
private void timer1_Tick(object sender, EventArgs e)
{
byte r = (byte)rnd.Next(0, 256);
byte g = (byte)rnd.Next(0, 256);
byte b = (byte)rnd.Next(0, 256);
System.Drawing.SolidBrush sb = new SolidBrush(Color.FromArgb(0,r,g,b));
}
Si lo dejo correr, lentamente consumirá memoria. Al observar el Administrador de tareas (no es la forma más precisa de medirlo), el uso de la memoria tiende a crecer (en mi máquina, construida con .NET 4.0 VS2010, versión) aproximadamente 20k bytes por actualización del Administrador de tareas (a la tasa de actualización más alta). Si llamo a Desechar en el pincel, el uso de la memoria tiende a aumentar en aproximadamente 8k por cada actualización del Administrador de tareas.
No es una prueba definitiva, pero parece apuntar a un mayor uso de la memoria a lo largo del tiempo si SolidBrush no se desecha. Curiosamente, ni los Mangos ni los Objetos GDI aumentaron en absoluto cuando se ejecutó la prueba (en cualquier caso). Basándome en la experiencia pasada con la pérdida de recursos de GDI, esperaba que los Objetos de GDI crecieran, especialmente en el caso de no disponer.
De todos modos, tal vez esto fue informativo, tal vez no.
Los objetos deben ser eliminados por la recolección de basura después de que hayan caído fuera del alcance, siempre que no estén referenciados por ninguna otra cosa.
La forma más sencilla de demostrar si esto está sucediendo o no (sin hacer ningún cambio en el código) es utilizar una gran cantidad de estos controles en un formulario y ver si la memoria utilizada por la aplicación sigue creciendo o permanece estable.
Necesito arreglar algunas fugas de memoria en una aplicación, así que estoy haciendo una investigación. En pocas palabras, parece que en la mayoría de los casos se sale con un uso de memoria ligeramente superior.
A continuación se muestra un caso patológico. Superviso mi aplicación de prueba en el administrador de tareas (lo sé, en crudo) y observé las columnas Memoria (Conjunto de trabajo privado), Manijas, Objetos USUARIO y Objetos GDI. Los clics de Button1 provocan un mayor uso de memoria que los clics de Button2.
Si hago clic de forma rápida y persistente en Button1, puedo provocar una ''excepción de System.OutOfMemory'' cuando la memoria llegue a aproximadamente 1.6GB. Button2 nunca supera los 12 MB, no importa cuán loca o persistentemente haga clic.
Estoy en una máquina win7 de 64 bits creando una aplicación winforms de perfil de cliente vs .NET 4. Obviamente, normalmente nunca construirías millones y millones de pinceles ...
Saludos David
private void button1_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128));
}
}
private void button2_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
using (SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128))) {
}
}
}
Por supuesto que sí importa. Es importante disponer de todas las instancias de clases que implementan IDisposable cuando ya no son necesarias. Las clases se definen como IDisposibles cuando utilizan recursos no administrados, es decir, recursos del sistema operativo que no son manejados por .NET Framework.
Si no desecha los objetos manualmente, estos recursos no administrados no se liberarán hasta que el recolector de basura invoque el finalizador de objetos (y esto solo ocurrirá si la clase ha implementado correctamente el patrón de Disposición ), que puede ser muy tarde ya que el recolector de basura solo se ejecuta cuando detecta una escasez de memoria real. Por lo tanto, cuando no está desechando objetos, está reteniendo recursos del sistema operativo que de otra manera podrían ser utilizados por otras aplicaciones.
Una discusión sobre este tema se puede encontrar aquí: http://agilology.blogspot.com/2009/01/why-dispose-is-necessary-and-other.html
Solo está perdiendo recursos no administrados (el lápiz / pincel) hasta que el recolector de basura reclama el objeto administrado correspondiente. Así que estás desperdiciando algunos manejadores que ya no se usan más.
No es un acuerdo demasiado grande porque Finalize()
llamará a Dispose()
pero la liberación de recursos no administrados generalmente se debe hacer más pronto que tarde.