C# 图像加载内存泄漏
我的应用程序存在内存泄漏问题,加载了大量图像。我对 C# 相当陌生,并且认为内存泄漏问题的日子已经过去了。我无法弄清楚问题 - 也许我正在使用一些未正确处理的非托管模块?
为了说明我的问题,我简化了导致问题的核心,并将其移至一个干净的项目中。请注意,这都是愚蠢的代码,并不反映它来自的原始应用程序。在测试应用程序中,我有 2 个按钮,触发两个事件。
按钮 1 - 创建:将对象设置到数据上下文。这将加载图像并通过将对象设置为 DataContext 来使它们保持活动状态:
var imgPath = @"C:\some_fixed_path\img.jpg";
DataContext = new SillyImageLoader(imgPath);
按钮 2 - CleanUp:我的理解是,如果我放开持有 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 上。仅保存位图,不会出现内存泄漏。我使用 BitmapSource 能够将其设置为图像的 Source 属性。我应该采取不同的做法吗?如果是这样 - 我仍然想知道内存泄漏的答案。
谢谢。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当您调用时,
将创建位图的副本。您需要保留对该对象的指针的引用并调用
它。
来自此处:
通过使用 BitmapImage 而不是 BitmapSource。这使您可以一步加载和创建。
When you call
a copy of the bitmap is created. You'll need to keep a reference to the pointer to that object and call
on it.
From here:
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.
您需要对从 GetHBitmap() 返回的
IntPtr
指针调用 GDIDeleteObject
方法。该方法返回的IntPtr
是指向内存中对象副本的指针。必须使用以下代码手动释放它:You need to call the GDI
DeleteObject
method on theIntPtr
pointer returned from GetHBitmap(). TheIntPtr
returned from the method is a pointer to the copy of the object in memory. This must be manually freed using the following code:看来,当您调用 GetHBitmap() 时,您负责释放该对象,
我猜测 BitmapSource 不负责释放该对象。
It seems that when you call GetHBitmap() you are responsible for freeing the object
I'm guessing that the BitmapSource doesn't take responsibility for freeing this object.