Reputation: 81
I build Console application for testing like this.
while (true)
{
var bmp = new Bitmap(1600, 1200);
//var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
After running, memory is increased up to over 2GB in very short time.
This is the log for the case of Bitmap
.
[2017-04-27 12:15:03][00:00:01.0150128] PrivateBytes : 1583.0MB, AllHeapsBytes : 0.0MB, Thread Count : 23, CPU Usage : 12%
[2017-04-27 12:15:04][00:00:02.0019966] PrivateBytes : 2150.0MB, AllHeapsBytes : 0.0MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:15:05][00:00:03.0030021] PrivateBytes : 500.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 26%
[2017-04-27 12:15:06][00:00:04.0040047] PrivateBytes : 1043.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 9%
[2017-04-27 12:15:07][00:00:05.0050024] PrivateBytes : 1601.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 7%
[2017-04-27 12:15:08][00:00:06.0060058] PrivateBytes : 2136.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 9%
[2017-04-27 12:15:09][00:00:07.0069981] PrivateBytes : 2695.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 14%
If I change Bitmap
to byte
array like this, the memory usage is stable.
while (true)
{
//var bmp = new Bitmap(1600, 1200);
var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
This is the log for the case of byte
array.
[2017-04-27 12:25:09][00:00:01.0080196] PrivateBytes : 63.0MB, AllHeapsBytes : 31.0MB, Thread Count : 23, CPU Usage : 11%
[2017-04-27 12:25:10][00:00:02.0020012] PrivateBytes : 66.0MB, AllHeapsBytes : 46.2MB, Thread Count : 24, CPU Usage : 18%
[2017-04-27 12:25:11][00:00:03.0030496] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 8%
[2017-04-27 12:25:12][00:00:04.0040530] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:25:13][00:00:05.0050386] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 11%
[2017-04-27 12:25:14][00:00:06.0060466] PrivateBytes : 52.0MB, AllHeapsBytes : 31.9MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:25:15][00:00:07.0070521] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 18%
I know that if I call bmp.Dispose()
it becomes stable memory usage.
I'm curious why Bitmap
is garbage-collected slowly and why not byte
array.
Upvotes: 1
Views: 1131
Reputation: 714
You should be using the using
to dispose the Bitmap
after it is used. This will give a good idea about why it is not collected as quickly.
"The .NET object representing a Bitmap
is only 48 bytes, but the associated unmanaged size may be significantly larger."
while (true)
{
using (var bmp = new Bitmap(1600, 1200))
{
// var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
}
Upvotes: 0
Reputation: 941407
Bitmap is a wrapper class for an unmanaged graphics library called gdiplus. It is modeled after the C++ wrapper for that library, the C# programmer made it close to a one-to-one match as you can tell from the documentation. The methods and properties are very small, they just pinvoke the library functions.
And the class object is very small, it has no fields of its own and inherits 3 from its base class Image. Just 24 bytes of GC heap required. You can create a lot of them before you'll trigger a garbage collection, close to a hundred thousand. Real storage is all unmanaged, inside the gdiplus library, proportional to Width * Height, depending on the pixel format of the image.
Releasing that unmanaged storage only happens when you call Dispose() or when the garbage collector runs the finalizer. .NET programmers almost always ignore Dispose/using when they first get started programming it. They invariably meet the maker at Bitmap.
Project > Properties > Resources is notable in this story. It provides very convenient syntax to use a bitmap resource in your program. But it is dangerous, just about nobody realizes that every time they use Properties.Resources.Somename in their code, they get a brand-new Bitmap object that must be disposed.
This went wrong so often that Microsoft decided for a completely different approach in WPF. Still a wrapper class for an unmanaged graphics library, a more recent one called WIC. But intentionally not implementing IDisposable. And a scheme to dispose it automatically. .NET framework code calling GC.Collect(), ouch. But much harder to find out that it didn't pan out well.
Upvotes: 6
Reputation: 1095
Bitmap is a finalizable object. It has unmanaged resources that need to be disposed properly. These resources are usually disposed when you call Dispose()
method. If Dispose()
was not called than CLR needs to dispose the resources by calling Bitmaps's finalizer first. This is done by a specific thread in CLR that is called finalization thread. This is an additional step that takes time. This is why you always need to Dispose()
bitmap if you do not need it. using
statement does it automatically. Byte array is a simple managed object. It does not require finalization
Upvotes: 1
Reputation: 4191
Because Bitmap consider as an image it is a big difference between Image and byte.
Upvotes: 0