2009-12-02 15 views
6

Soy un recién llegado en C#. Tengo que actualizar varias veces un cuadro de imagen GUI en un hilo de trabajo. La imagen se adquiere desde una cámara que está sondeando un controlador con un método GetImage que recupera la imagen que se mostrará. Incluso si asigno el mapa de bits utilizando la directiva "using" y llamo explícitamente a G.C, la memoria parece que nunca se desasigna.C# picturebox liberación de memoria problema

El subproceso de trabajo es algo como esto:

while (true) 
    { 
     // request image with IR signal values (array of UInt16) 
     image = axLVCam.GetImage(0); 
     lut = axLVCam.GetLUT(1); 
     DrawPicture(image, lut); 
     //GC.Collect(); 

    } 

Mientras que el método DrawPicture es algo así como

public void DrawPicture(object image, object lut) 
{ 

    [...] 

    // We have an image - cast it to proper type 
    System.UInt16[,] im = image as System.UInt16[,]; 
    float[] lutTempConversion = lut as float[]; 

    int lngWidthIrImage = im.GetLength(0); 
    int lngHeightIrImage = im.GetLength(1); 

    using (Bitmap bmp = new Bitmap(lngWidthIrImage, lngHeightIrImage)) { 

     [...many operation on bitmap pixel...] 

     // Bitmap is ready - update image control 

     //SetControlPropertyThreadSafe(tempTxtBox, "Text", string.Format("{0:0.#}", lutTempConversion[im[160, 100]])); 

     //tempTxtBox.Text = string.Format("{0:00000}", im[160, 100]); 
     //System.Drawing.Image.FromHbitmap(bmp.GetHbitmap()); 
     pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap()); 
    } 
} 

problemas surge con la

pic.Image = System.Drawing .Image.FromHbitmap (bmp.GetHbitmap());

De hecho, al comentar esa línea de código, la recolección de basura funciona como lo haría. mejor, el problema parece ser con

System.Drawing.Image.FromHbitmap (bmp.GetHbitmap())

Algún consejo para resolver esta pérdida de memoria?

¡Muchas gracias!

Respuesta

12

Image implementa IDisposable, por lo que debe llamar Dispose en cada Image instancia que se crea, cuando ya no se necesita. Se podría tratar de sustituir esta línea en su código:

pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap()); 

Con esta:

if (pic.Image != null) 
{ 
    pic.Image.Dispose(); 
} 
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap()); 

Este dispondrá la imagen anterior (si lo hay) antes de asignar la nueva.

+0

El problema es que no está deshaciéndose de un objeto GDI. –

+0

@Yannick: buen punto. Imagino que ambos problemas crean problemas para el recolector de basura. –

+0

De hecho, al invocar Dispose on Image descarta los recursos no administrados que se asignan al llamar a 'FromHbitmap()'. Sin embargo, la solución actual ignora el objeto GDI no administrado creado al llamar a 'GetHbitmap()'. –

7

La cosa es, usted está haciendo un mapa de bits de GDI bmp con GetHbitmap, que de acuerdo con MSDN:

Usted es responsable de llamar al método GDI DeleteObject para liberar la memoria utilizada por la GDI objeto de mapa de bits

Luego, el método FromHbitmap hace una copia del mapa de bits GDI; para que pueda liberar el mapa de bits GDI entrante utilizando el método GDI DeleteObject inmediatamente después de crear la nueva imagen.

Así que básicamente me gustaría añadir:

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); 

... 

IntPtr gdiBitmap = bmp.GetHbitmap(); 

// Release the copied GDI bitmap 
if (pic.Image != null) 
{ 
    pic.Image.Dispose(); 
} 

pic.Image = System.Drawing.Image.FromHbitmap(gdiBitmap); 

// Release the current GDI bitmap 
DeleteObject(gdiBitmap); 

No estoy seguro de si necesita el mapa de bits GDI para llevar a cabo algún tipo de transformación. En caso de que no se puede simplemente asignar el mapa de bits a la propiedad de imagen del cuadro de imagen, e ignorar la primera solución:

// Since we're not using unmanaged resources anymore, explicitly disposing 
// the Image only results in more immediate garbage collection, there wouldn't 
// actually be a memory leak if you forget to dispose. 
if (pic.Image != null) 
{ 
    pic.Image.Dispose(); 
} 

pic.Image = bmp; 
3

Hay varias maneras de liberar una imagen de pbox.Recomiendo encarecidamente que la manera sea que no utilices pbox.Image = Image.FromFile.... Si no usas FileStream y quieres leerlo desde el archivo usa la clase BitMap. De esta manera:

Bitmap bmp = new Bitmap(fileName); 
pbox.Image = bmp; // notice that we used bitmap class to initialize pbox. 

... y luego desea liberar el archivo de imagen bmp.Dispose();
Ahora se puede borrar, mover o como se quiera al archivo.