.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:
- You open image "ruta2"
- You create a new image
- You draw the contents of "ruta2" inside that new image
- 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
How Math.Pow (And So On) Actually Works
Replace Unicode Escape Sequences in a String
How to Add a String to a String[] Array? There's No .Add Function
Iis Wcf Service Hosting VS Windows Service
What Is the C# Equivalent of Nan or Isnumeric
How to Display the Displayattribute.Description Attribute Value
Open Source Cad Drawing (Dwg) Library in C#
Run Two Winform Windows Simultaneously
Why Does Visual Studio Type a Newly Minted Array as Nullable
How to Convert Securestring to System.String
Compare Two Lists for Differences
How to Get the Network Interface and Its Right Ipv4 Address