I can't understand what is leaking here
using GDI = System.Drawing;
public partial class MainWindow : Window
{
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr obj);
public MainWindow()
{
InitializeComponent();
var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(50) };
timer.Tick += (s, a) =>
{
using (var bitmap = new GDI.Bitmap(1000, 1000))
{
var hbitmap = bitmap.GetHbitmap();
var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
image.Freeze();
DeleteObject(hbitmap);
}
};
timer.Start();
}
bitmap
? Disposed. hbitmap
? Deleted. image
? Frozen and it's not IDisposable
.
The fact is, this application will crash (on my PC after just ~20 seconds of running)
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.
Any ideas?
As far I can tell, there are no leaks going on. The problem is that you're allocating big C# objects fast and garbage collector kicks in way too late?
Here are few relevant topics:
Avoiding OutOfMemoryException during large, fast and frequent memory allocations in C#
and here is useful thread:
Garbage Collection not happening even when needed
If you kick GC.Collect(with generations 0..3), your memory consumption will be fixed:
while (true)
{
Thread.Sleep(5);
using (var bitmap = new GDI.Bitmap(1000, 1000))
{
var hbitmap = bitmap.GetHbitmap();
var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
image.Freeze();
DeleteObject(hbitmap);
}
Console.WriteLine("Current memory consumption" + GC.GetTotalMemory(false));
GC.Collect(3);
}
and output:
Current memory consumption156572
Current memory consumption156572
Current memory consumption156572
Current memory consumption156572
The real problem is that GC does not know about your unmanaged allocations, even if you free them. You need to add memory pressure and let the GC know about it:
var width = 1000;
var height = 1000;
using (var bitmap = new GDI.Bitmap(width, height))
{
var hbitmap = bitmap.GetHbitmap();
var allocatedSize = width*height*4; // each pixel takes ~4 bytes?!
GC.AddMemoryPressure(allocatedSize);
var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
image.Freeze();
DeleteObject(hbitmap);
GC.RemoveMemoryPressure(allocatedSize);
}
Letting GC know about underlying unmanaged memory helps to make sure GC kicks in at right places.
It is possible that the Garbage Collector is not freeing up the disposed bitmaps fast enough. You are creating 400 1000*1000 bitmaps every 20 seconds which could consume up to 1.6GB of memory. Maybe try adding a second timer that runs every 1000 milliseconds that makes a call to GC.Collect().