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

16

系统: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,我就会将其Freezing。

4)删除所有对使用该源的图像以及对源本身的引用。

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

希望找出为什么WPF会挂起这些图像所需的内存,并找到一种可能的解决方案以确保正确回收用于加载它们的内存。

1个回答

28

你确实在这方面做了很多工作。我认为主要问题是BitmapCacheOption.None无法阻止底层的BitmapDecoder被缓存。

有几个棘手的解决方案,比如执行GC.Collect()、从300个不同的Uri加载300张小图片,并再次调用GC.Collect(),但简单的解决方法很直接:

不要从Uri加载,只需构造一个流并将其传递给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,加载的图像仍会被插入缓存中。区别在于忽略缓存中的现有图像。

BitmapSource source = new BitmapSource() 无法编译,我不确定原因。抛出以下错误: 错误4:无法创建抽象类或接口“System.Windows.Media.Imaging.BitmapSource”的实例。 - discorax
啊..当我使用BitmapImage而不是BitmapSource时,它可以编译。那么,这会引起什么问题呢? :) - discorax
这种方法目前看起来很有前途。我会继续测试。 - discorax
@RayBurns:请验证此帖子。 http://stackoverflow.com/questions/18872636/out-of-memory-exception-while-printing-in-wpf 我尝试了你的方法......但是出现了新的异常。 - Uthistran Selvaraj.
2
对于任何想知道 ... 应该是什么的人,如果你正在尝试从本地磁盘加载图像,请尝试使用 new FileStream(path, FileMode.Open) - devuxer
只需构建一个流并将其传递给 BitmapFrame 的构造函数即可。@RayBurns,这是什么意思? - Dvor_nik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接