如何确保 WPF 从内存中释放大的 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)我将删除对使用源的图像的所有引用以及对源本身的任何引用。
5)上述步骤完成后手动调用GC.Collect()。
希望弄清楚为什么 WPF 会挂在这些图像的内存上,并找到一个可能的解决方案来确保正确恢复用于加载它们的内存。
System: Windows XP SP3, .NET 3.5, 4GB RAM, Dual 1.6gHz
I have a WPF application that loads and transitions (using Storyboard animations) extremely large PNGs. These PNGs are 8190x1080 in resolution. As the application runs it appears to cache the images and the system Memory slowly creeps up. Eventually it chokes the system and throws the OutOfMemoryException.
Here are the steps I am currently taking to try to solve this:
1)I am removing the BitmapSource objects from the app
2)I am setting the BitmapSource BitmapCacheOption to None when I load the BitmapSource
3)I am Freezing the BitmapSource once it's loaded.
4)I am deleting all references to the Image that uses the source as well as any references to the source itself.
5)Manually calling GC.Collect() after above steps have completed.
Hoping to figure out why WPF is hanging onto memory for these images and a possible solution to ensure that the memory used to load them is properly recovered.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您肯定在这方面投入了大量的工作。我认为主要问题是 BitmapCacheOption.None 不会阻止底层 BitmapDecoder 被缓存。
有几个棘手的解决方案,例如执行 GC.Collect()、从 300 个不同的 Uri 加载 300 个小图像,然后再次调用 GC.Collect(),但简单的解决方案很简单:
不用从 Uri 加载,只需构造一个 Stream 并将其传递给 BitmapFrame 的构造函数:
这应该起作用的原因是从流加载会完全禁用缓存。不仅顶级源没有被缓存,而且内部解码器也没有被缓存。
为什么选择 BitmapCacheOption.OnLoad?这似乎违反直觉,但该标志有两个作用:如果可以缓存,它会启用缓存,并且会导致加载发生在 EndInit() 处。在我们的例子中,缓存是不可能的,所以它所做的一切都会导致加载立即发生。
显然,您需要在 UI 线程之外运行此代码,然后冻结 BitmapSource,以便可以将其移动。
您可能还想知道为什么我没有使用 BitmapCreateOptions.IgnoreImageCache。除了在没有给定 URI 的情况下不可能进行缓存这一事实之外,IgnoreImageCache 不会完全忽略图像缓存:它只是在读取时忽略它。所以即使设置了IgnoreImageCache,加载的图像仍然会插入到缓存中。不同之处在于缓存中的现有图像将被忽略。
You certainly have put in a lot of work on this. I think the main problem is that BitmapCacheOption.None doesn't prevent the underlying BitmapDecoder(s) from being cached.
There are several tricky solutions to this such as doing a GC.Collect(), loading 300 small images from 300 different Uris, and calling GC.Collect() again, but the simple one is straightforward:
Instead of loading from a Uri, just construct a Stream and pass it to BitmapFrame's constructor:
The reason this should work is that loading from a stream completely disables the cache. Not only is the top-level source not cached, but none of the internal decoders are cached either.
Why BitmapCacheOption.OnLoad? It seems counterintuitive, but this flag has two effects: It enables caching if caching is possible, and it causes the load to happen at EndInit(). In our case caching is impossible, so all it does it cause the load to happen immediately.
Obviously you'll want to run this code off your UI thread, then freeze the BitmapSource so you can move it over.
You may also wonder why I didn't use BitmapCreateOptions.IgnoreImageCache. Other than the fact that caching is impossible any with no URI given, the IgnoreImageCache doesn't completely ignore the image cache: It only ignores it for reading. So even if IgnoreImageCache is set, the loaded image is still inserted into the cache. The difference is that the existing image in the cache is ignored.