使用C#加载内存泄漏的图像

我的应用程序中存在内存泄漏问题,它会加载大量图像。 我对C#很陌生,并认为我的内存泄漏问题已经过去了。 我无法弄清楚问题 – 也许我正在使用一些我无法正确处理的非托管模块?

为了说明我的问题,我简化了导致问题的核心,并将其转移到一个干净的项目中。 请注意,这都是愚蠢的代码,不能反映它来自的原始应用程序。 在测试应用程序中,我有2个按钮,触发两个事件。

按钮1 – 创建:将对象设置为datacontext。 这将通过将对象设置为DataContext来加载图像并使它们保持活动状态:

var imgPath = @"C:\some_fixed_path\img.jpg"; DataContext = new SillyImageLoader(imgPath); 

按钮2 – 清理:我的理解是,如果我放开持有SillyImageLoader并再次保存图像的引用,那么这将被删除。 我还明确地触发垃圾收集,只是为了在删除引用后立即查看内存量。

 DataContext = null; System.GC.Collect(); 

测试时我正在加载一个974KB的jpeg图像。 保持30位图表示可以将我的应用程序的内存使用量从大约18MB增加到大约562MB。 好。 但是当我清理时,内存只下降到~292MB。 如果我重复Create + CleanUp,我还剩下另外250MB内存。 所以显然有人仍然持有某种东西。

这是SillyImageLoader代码:

 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()); } } } } 

有任何想法吗? 问题似乎与BitmapSource有关。 仅保留Bitmap没有内存泄漏。 我正在使用BitmapSource将其设置为Image的Source属性。 我应该这样做吗? 如果是这样 – 我仍然想知道内存泄漏的答案。

谢谢。

你打电话的时候

 bmp.GetHbitmap() 

创建位图的副本。 您需要保持对该对象的指针的引用并调用

 DeleteObject(...) 

在上面。

从这里 :

备注

您负责调用GDI DeleteObject方法来释放GDI位图对象使用的内存。


您可以通过使用BitmapImage而不是BitmapSource来节省复制位图的麻烦(和开销)。 这允许您一步加载和创建。

您需要在从GetHBitmap()返回的IntPtr指针上调用GDI DeleteObject方法。 从方法返回的IntPtr是指向内存中对象副本的指针。 必须使用以下代码手动释放:

 [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; } 

看来当你调用GetHBitmap()时,你负责释放对象

 [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); } 

我猜测BitmapSource不负责释放这个对象。