.NET内存问题加载~40张图像,内存未回收,可能是由于LOH碎片造成的

Ed *_* S. 32 .net c# wpf memory-leaks memory-management

好吧,这是我第一次尝试内存分析.NET应用程序(我已经完成了CPU调优),我在这里遇到了一些障碍.

我在我的应用程序中有一个视图,每页加载40个图像(最大),每个运行大约3MB.最大页数为10.由于我不想一次保留400张图像或1.2GB内存,因此在更改页面时将每个图像设置为空.

现在,起初我认为我必须对这些图像进行过时的引用.我下载了ANTS profiler(伟大的工具BTW)并进行了一些测试.对象生命周期图表告诉我,除了父类中的单个引用之外,我没有对这些图像的任何引用(这是设计的,也通过精心梳理我的代码来确认):

在此输入图像描述

父类SlideViewModelBase永远存在于缓存中,但是MacroImage当页面更改时,属性设置为null.我没有看到任何迹象表明这些物体应该保持比预期更长的时间.

我接下来看一下大对象堆和内存使用情况.在查看三页图像后,我分配了691.9MB的非托管内存和LOH上的442.3MB. System.Byte[]来自我System.Drawing.BitmapBitmapImage转换,几乎占据了所有的LOH空间.这是我的转换代码:

public static BitmapSource ToBmpSrc( this Bitmap b )
{
    var bi = new BitmapImage();
    var ms = new MemoryStream();
    bi.CacheOption = BitmapCacheOption.OnLoad;
    b.Save( ms,  ImageFormat.Bmp );
    ms.Position = 0;
    bi.BeginInit();
    ms.Seek( 0, SeekOrigin.Begin );
    bi.StreamSource = ms;
    bi.EndInit();
    return bi;
}
Run Code Online (Sandbox Code Playgroud)

我很难找到所有非托管内存的去向.我System.Drawing.Bitmap起初怀疑这些物体,但是ANTS并没有显示它们在附近,我也进行了一次测试,我确保所有这些都被处理掉了,并没有什么区别.所以我还没有弄清楚所有非托管内存的来源.

我目前的两个理论是:

  1. LOH碎片化.如果我离开分页视图并单击几个按钮,大约一半的~1.5GB被回收.仍然太多,但仍然很有趣.
  2. 一些奇怪的WPF绑定的东西.我们确实使用数据绑定来显示这些图像,而且我对这些WPF控件如何工作的细节并不陌生.

如果有人有任何理论或剖析技巧,我将非常感激,因为(当然)我们正处在一个紧迫的期限内,我正在争先恐后地完成最后一部分工作.我认为通过追踪C++中的内存泄漏已经被宠坏了......谁会想到?

如果您需要更多信息或希望我尝试别的,请询问.对于这里的墙上文字感到抱歉,我尽量保持简洁.

Opp*_*nal 35

这篇博文似乎描述了你所看到的内容,建议的解决方案是创建一个包含另一个流的Stream实现.

此包装类的Dispose方法需要释放包装的流,以便可以对其进行垃圾回收.一旦使用此包装器流初始化BitmapImage,就可以释放包装器流,释放底层流,并允许释放大字节数组本身.

BitmapImage保留对源流的引用,以使MemoryStream对象保持活动状态.不幸的是,即使已经调用了MemoryStream.Dispose,它也不会释放内存流包装的字节数组.因此,在这种情况下,位图是引用流,它引用缓冲区,这可能占用大对象堆上的大量空间.没有真正的内存泄漏; 当没有更多对位图的引用时,所有这些对象将(最终)被垃圾收集.但由于位图已经制作了自己的图像私有副本(用于渲染),因此将现在不必要的位图原始副本仍然存储在内存中似乎相当浪费.

另外,您使用的是什么版本的.NET?在.NET 3.5 SP1之前,存在一个已知问题,即BitmapImage可能导致内存泄漏.解决方法是在BitmapImage上调用Freeze.

  • 这就是确切的问题,我希望我能再给你1000分.在我的一个测试中手动处理后,我认为底层流不是问题,但我想这就是我假设我知道我到底在做什么= D.再次感谢. (4认同)
  • 两个链接都失效了。知道在哪里可以找到该内容吗? (2认同)