I have a memory leak issue in my application which loads a large amount of images. I'm rather new to C#, and thought my days of memory leak issues were past. I can't figure out the problem - maybe I'm using some unmanaged modules which I'm not handle correctly?
To illustrate my problem I've simplified the core of what causes the problem and moved this to a clean project. Note that this is all silly code which doesn't reflect the original application it came from. In the test application I have 2 buttons, triggering two events.
Button 1 - Create: Setting an object to the datacontext. This will load the images and keep them alive by setting the object to the DataContext:
var imgPath = @"C:\some_fixed_path\img.jpg";
DataContext = new SillyImageLoader(imgPath);
Button 2 - CleanUp: My understanding is that if I let go of the reference holding the SillyImageLoader which again holds the images, then this will be deleted. I also explicitly trigger garbage collection just to see immediately the amount of memory after dropping the reference.
DataContext = null;
System.GC.Collect();
When testing I'm loading a 974KB jpeg image. Holding 30 bitmap representations of this boosts the memory usage of my application from ~18MB to ~562MB. Ok. But when I hit cleanup the memory drops only to ~292MB. If I repeat Create+CleanUp I'm left with another ~250MB memory. So obviously something is still held by someone.
Here is the SillyImageLoader-code:
namespace MemoryLeakTest
{
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
public class SillyImageLoader
{
private BitmapSource[] _images;
public SillyImageLoader(string path)
{
DummyLoad(path);
}
private void DummyLoad(string path)
{
const int numberOfCopies = 30;
_images = new BitmapSource[numberOfCopies];
for (int i = 0; i < numberOfCopies; i++)
{
_images[i] = LoadImage(path);
}
}
private static BitmapSource LoadImage(string path)
{
using (var bmp = new Bitmap(path))
{
return Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
}
}
Any ideas? The problem seems to be with the BitmapSource. Holding only the Bitmap there is no memory leak. I am using BitmapSource to be able to set this to the Source property of an Image. Should I do this differently? If so - I'd still like to know the answer the memory leak.
Thanks.
When you call
bmp.GetHbitmap()
a copy of the bitmap is created. You'll need to keep a reference to the pointer to that object and call
DeleteObject(...)
on it.
From here:
Remarks
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
You may be able to save yourself the headache (and overhead) of copying the bitmap by using BitmapImage instead of BitmapSource. This allows you to load and create in one step.
You need to call the GDI DeleteObject
method on the IntPtr
pointer returned from GetHBitmap(). The IntPtr
returned from the method is a pointer to the copy of the object in memory. This must be manually freed using the following code:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private static BitmapSource LoadImage(string path)
{
BitmapSource source;
using (var bmp = new Bitmap(path))
{
IntPtr hbmp = bmp.GetHbitmap();
source = Imaging.CreateBitmapSourceFromHBitmap(
hbmp,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hbmp);
}
return source;
}
It seems that when you call GetHBitmap() you are responsible for freeing the object
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private void DoGetHbitmap()
{
Bitmap bm = new Bitmap("Image.jpg");
IntPtr hBitmap = bm.GetHbitmap();
DeleteObject(hBitmap);
}
I'm guessing that the BitmapSource doesn't take responsibility for freeing this object.
来源:https://stackoverflow.com/questions/1714841/image-loading-memory-leak-with-c-sharp