views:

201

answers:

3

So I've finally located a problem I have with growing memory consumption. It's the class below, which for some reason doesn't get garbage collected. What would be the problem? The idea of the FastBitmap class is to lock the bitmap data of a bitmap image once to avoid locking/unlocking on each call to GetPixel/SetPixel.

 public unsafe class FastBitmap
 {
  private Bitmap subject;
  private int subject_width;
  private BitmapData bitmap_data = null;
  private Byte* p_base = null;

  public FastBitmap(Bitmap subject_bitmap)
  {
   this.subject = subject_bitmap;
   try
   {
    LockBitmap();
   }
   catch (Exception ex)
   {
    throw ex;
   }
  }

  public void Release()
  {
   try
   {
    UnlockBitmap();
   }
   catch (Exception ex)
   {
    throw ex;
   }
  }

  public Bitmap Bitmap
  {
   get { return subject; }
  }

  public void LockBitmap()
  {
   GraphicsUnit unit = GraphicsUnit.Pixel;
   RectangleF boundsF = subject.GetBounds(ref unit);
   Rectangle bounds = new Rectangle((int)boundsF.X, (int)boundsF.Y, (int)boundsF.Width, (int)boundsF.Height);
   subject_width = (int)boundsF.Width * sizeof(int);

   if (subject_width % 4 != 0)
   {
    subject_width = 4 * (subject_width / 4 + 1);
   }

   bitmap_data = subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
   p_base = (Byte*)bitmap_data.Scan0.ToPointer();
  }

  private void UnlockBitmap()
  {
   if (bitmap_data == null) return;
   subject.UnlockBits(bitmap_data); bitmap_data = null; p_base = null;
  }
 }

EDIT

Here's how it does get properly collected..

public unsafe class FastBitmap : IDisposable
{
    private Bitmap subject;
    private int subject_width;
    private BitmapData bitmap_data = null;
    private Byte* p_base = null;

    public FastBitmap(Bitmap subject_bitmap)
    {
        this.subject = subject_bitmap;
        try
        {
            LockBitmap();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

 public void Dispose()
 {
  Dispose(true);

  GC.SuppressFinalize(this);
 }

 private bool disposed = false;
 protected virtual void Dispose(bool disposing)
 {
  if (!this.disposed)
  {
   if (disposing)
   {
    UnlockBitmap();
    Bitmap.Dispose();
   }

   subject = null;
   bitmap_data = null;
   p_base = null;

   disposed = true;
  }
 }

 ~FastBitmap()
 {
  Dispose(false);
 }

    public Bitmap Bitmap
    {
        get { return subject; }
    }

    public void LockBitmap()
    {
        GraphicsUnit unit = GraphicsUnit.Pixel;
        RectangleF boundsF = subject.GetBounds(ref unit);
        Rectangle bounds = new Rectangle((int)boundsF.X, (int)boundsF.Y, (int)boundsF.Width, (int)boundsF.Height);
        subject_width = (int)boundsF.Width * sizeof(int);

        if (subject_width % 4 != 0)
        {
            subject_width = 4 * (subject_width / 4 + 1);
        }

        bitmap_data = subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
        p_base = (Byte*)bitmap_data.Scan0.ToPointer();
    }

    public void UnlockBitmap()
    {
        if (bitmap_data == null) return;
        subject.UnlockBits(bitmap_data); bitmap_data = null; p_base = null;
    }
}
+1  A: 

At a first glance i would say that you want to look into implementing the IDisposable interface on the class so that you can be sure to free up resources that are being used by the class.

Mitchel Sellers
+2  A: 

A couple points:

  • Your class hold access to pinned data. The garbage collector works by moving structures around in memory. So long as the bitmap has locked its bits, the garbage collector can't do anything with it.

  • Once you have Released the FastBitmap, I'm afraid that GDI+ may still be hanging onto the bits of data. GDI+ is a native library that does not interact with the garbage collector.

  • You need to release (dispose of) the GDI+ Bitmap too. Just call subject.Dispose() in Release.

As Mitchel mentioned, it would be nice to make your FastBitmap implement IDisposable and rename Release to dispose. This will allow you to use using statements in your code to make sure that the data is freed deterministically.

Frank Krueger
A: 

If this class isn't being Garbage Collected, then something else still has a reference to it. While the internal data may be what is keeping it locked, I'd look elsewhere first.

Joel Lucsy