16

系统:Windows XP SP3、.NET 3.5、4GB RAM、双 1.6GHz

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

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

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

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

3)一旦加载,我将冻结 BitmapSource。

4)我正在删除对使用源的图像的所有引用以及对源本身的任何引用。

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

希望弄清楚为什么 WPF 会为这些图像挂在内存上,以及一个可能的解决方案,以确保正确恢复用于加载它们的内存。

4

1 回答 1

28

你当然为此付出了很多努力。我认为主要问题是 BitmapCacheOption.None 不会阻止底层 BitmapDecoder(s) 被缓存。

对此有几个棘手的解决方案,例如执行 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,加载的图像仍然会插入到缓存中。不同之处在于缓存中的现有图像被忽略。

于 2009-11-06T05:01:25.697 回答