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

时间:2009-11-05 23:52:11

标签: 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会为这些图像挂起内存,以及确保用于加载它们的内存得到正确恢复的可能解决方案。

1 个答案:

答案 0 :(得分:23)

你当然在这方面做了很多工作。我认为主要问题是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();
}

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

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

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

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