你如何确保WPF从内存中释放大型BitmapSource?

dis*_*rax 14 wpf memory-leaks bitmap bitmapsource

系统:Windows XP SP3,.NET 3.5,4GB RAM,双1.6gHz

我有一个WPF应用程序加载和转换(使用Storyboard动画)非常大的PNG.这些PNG的分辨率为8190x1080.当应用程序运行时,它似乎缓存图像,系统内存缓慢爬升.最终它会阻塞系统并抛出OutOfMemoryException.

以下是我目前正在尝试解决此问题的步骤:

1)我从应用程序中删除BitmapSource对象

2)我在加载BitmapSource时将BitmapSource BitmapCacheOption设置为None

3)我在加载后冻结BitmapSource.

4)我将删除对使用源的Image的所有引用以及对源本身的任何引用.

5)完成上述步骤后,手动调用GC.Collect().

希望找出为什么WPF挂在这些映像的内存上,以及确保用于加载它们的内存得到正确恢复的可能解决方案.

Ray*_*rns 25

你当然在这方面做了很多工作.我认为主要问题是BitmapCacheOption.None不会阻止底层BitmapDecoder被缓存.

有几个棘手的解决方案,例如执行GC.Collect(),从300个不同的Uris加载300个小图像,再次调用GC.Collect(),但简单的解决方案很简单:

而不是从Uri加载,只需构造一个Stream并将其传递给BitmapFrame的构造函数:

var source = new BitmapImage();
using(Stream stream = ...)
{
  source.BeginInit();
  source.StreamSource = stream;
  source.CacheOption = BitmapCacheOption.OnLoad;    // not a mistake - see below
  source.EndInit();
}
Run Code Online (Sandbox Code Playgroud)

这应该工作的原因是从流加载完全禁用缓存.顶级源不仅没有缓存,也没有内部解码器被缓存.

为什么BitmapCacheOption.OnLoad?这似乎违反直觉,但是这个标志有两个作用:如果可以进行缓存,它会启用缓存,并且它会导致在EndInit()中发生加载.在我们的例子中,缓存是不可能的,所以它只会导致负载立即发生.

显然,您需要从UI线程运行此代码,然后冻结BitmapSource,以便将其移动.

您可能也想知道为什么我没有使用BitmapCreateOptions.IgnoreImageCache.除了没有给出任何URI的高速缓存这一事实外,IgnoreImageCache不会完全忽略图像高速缓存:它只会忽略它以进行读取.因此,即使设置了IgnoreImageCache,加载的图像仍会插入缓存中.不同之处在于忽略缓存中的现有图像.

  • 对于任何想知道 `...` 应该是什么的人,如果您尝试从本地磁盘加载图像,请尝试使用 `new FileStream(path, FileMode.Open)`。 (2认同)