.Net and Bitmap Not Automatically Disposed by Gc When There Is No Memory Left

.Net and Bitmap not automatically disposed by GC when there is no memory left

The .NET Bitmap class "encapsulates a GDI+ bitmap", that means you should call Dispose on a Bitmap when you are finished with it,

"Always call Dispose before you
release your last reference to the
Image. Otherwise, the resources it is
using will not be freed until the
garbage collector calls the Image
object's Finalize method."

Are bitmaps somehow a system resource?

The MSDN states that Bitmap encapsulates a GDI+ object. So I would expect that you have to call Dispose() to release these system resources when you no longer need them.

EDIT: Bitmap being a subclass of Image, the following MSDN statement applies

Always call Dispose before you release your last reference to the Image. Otherwise, the resources it is using will not be freed until the garbage collector calls the Image object's Finalize method.

Why does this method consume so much memory? Or rather, why isn't GC freeing the memory?

The issue is that you are not calling Dispose on your Bitmap. A Bitmap in .Net is essentially just a small managed object that wraps around a large unmanged GDI+ bitmap (source). It is this GDI+ bitmap that is storing the pixel data and, therefore, responsible for the memory consumption. The managed .Net Bitmap, being just a wrapper, is much smaller in comparison.

The reason GC.Collect() is freeing the memory is because you are forcing the GC to free all those bitmaps that are no longer being referenced which is in turn releasing all the unmanaged memory they are pointing to. The reason the GC is not doing this for you automatically is because the GC only tracks managed memory not unmanaged memory. So from the GC's point of view all these managed bitmaps you have left laying around are small and not taking up a lot of room so it is not important that it collect them. When, in fact, they are quite large.
From the MSDN:

If a small managed object allocates a large amount of unmanaged memory, the runtime takes into account only the managed memory, and thus underestimates the urgency of scheduling garbage collection.

By calling Dispose on your Bitmap when you no longer need it you will immediately free the unmanaged memory being used by the GDI+ bitmap. The managed .Net Bitmap will still hang around until the GC gets around to collecting it but who cares, that's tiny.

Generic GDI+ Error: Avoided when calling GC.Collect()

First, it would be nice if you were using the "using" keyword to scope the use of your disposable objects (like Bitmap and Graphics), instead of calling Dispose() manually on each. Using "using" is better for lots of reasons like cleaning up stuff when an exception is thrown, but it also greatly helps for code readability.

Second, GDI brushes are also IDisposable objects, so you shouldn't create-and-forget them. Do this instead:

using (var brush = new SolidBrush(Color.White))
{
g.FillRectangle(brush, 0, 0, width, height)
}

...or better yet, create your brushes at the start of your application, and hang on to them until the end (but don't forget to dispose of them too). IIRC, creating/disposing brushes impacts performance quite a lot if done frequently.

Third, I believe your bug is in the 2nd section:

  1. You open image "ruta2"
  2. You create a new image
  3. You draw the contents of "ruta2" inside that new image
  4. You save that new thing on top of the "ruta2" file, but the original image from step 1 is still loaded, and probably has some handle on the "ruta2" file, so you need to dispose of it before you overwrite the file.

If you refactor that 2nd section like this, it should work:

using (var b = new Bitmap(OtherWidth, OtherHeight))
using (var g = Graphics.FromImage(b))
{
using (var brush = new SolidBrush(Color.Red))
{
g.FillRectangle(brush, 0, 0, b.Width, b.Height);
}
using (var pic = new Bitmap(ruta2))
{
g.DrawImage(pic, 0, 0, pic.Height, pic.Width);
}
b.Save(ruta2, ImageFormat.Jpeg);
}

Garbage Collector too slow in foreach loop?

Perhaps you should work with using:

var files = Directory.GetFiles(path).ToList();

foreach (var file in files)
{
using (Image image = new Bitmap(file))
{
// do work
}
}

This way the Bitmap will be disposed after the iteration

Does Bitmap.LockBits pin a bitmap into memory?

GCHandle.Alloc is a more generic method, that allows you to allocate a handle to any managed object and pin it in memory (or not). Pinning memory prevents GC from moving it around, which is especially useful when you have to pass some data, for example an array, to a unmanaged code.

GCHandle.Alloc will not help you access bitmap's data in any way, because pinning this object will just prevent the managed object from moving around (the Bitmap object) (and being garbage collected).

Bitmap however is a wrapper around native GDI+'s BITMAP structure. It doesn't keep data in any managed array that you would have to pin, it just managed a native handle to GDI+ bitmap object. Because of that Bitmap.LockBits is a way of telling this bitmap that you are interested in accessing it's memory, and it's just a wrapper around GdipBitmapLockBits function. So your need of calling it has more to do with the fact that you are working with GDI+ bitmaps than with the fact, that you're working in managed environment with GC.

Once you have used LockBits you should be able to access it's memory using pointers through BitmapData.Scan0 - it's an address of first byte of data. You should not have problems as long, as you do not access memory behind BitmapData.Scan0 + Height * Stride.

And rememberto UnlockBits when you are done.



Related Topics



Leave a reply



Submit