在WPF中加载多张图片时如何避免内存膨胀问题

4
我有一个非常简单的WPF应用程序,用于逐个预览给定文件夹中的图像。您可以将其视为Windows图像查看器克隆版。该应用程序具有PreviewKeyUp事件,用于在按下左箭头或右箭头键时加载文件夹中的上一张或下一张图像。
<Grid>
    <Image x:Name="CurrentImage" />
</Grid>

private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
{
    switch (e.Key)
    {
        case Key.Left:
            DecreaseIndex();
            break;
        case Key.Right:
            IncreaseIndex();
            break;
    }

    var currentFile = GetCurrentFile();
    CurrentImage.Source = new BitmapImage(new Uri(currentFile));
}

我试图解决的问题是,在加载多个图像直到垃圾收集发生时,会出现大量内存膨胀。您可以在我拍摄的应用程序内存使用截图中看到这一点。在垃圾收集发生之前,它很常见超过300 MB。

screenshot

我尝试使用using语句来包装图像,但这不起作用,因为BitmapImage没有实现IDisposable。

using (var image = new BitmapImage(new Uri(currentFile)))
{
    CurrentImage.Source = image;
}

当我在我的应用程序中加载多个图像时,我应该怎么做才能防止内存膨胀?


你可能也想看一下这个链接:https://dev59.com/0m025IYBdhLWcg3wAg7p#6271982 - Clemens
2个回答

7
当您提到预览时,您可能不需要完整的图像大小。因此,除了调用 Freeze 之外,您还可以设置BitmapImage的 DecodePixelWidth DecodePixelHeight 属性。
我还建议直接从 FileStream 加载图像,而不是从 Uri 加载。请注意, UriCachePolicy 的在线文档说:

...表示来自HTTP源的图像的缓存策略的值。

因此,它可能无法使用本地文件Uri。
为了安全起见,您可以这样做:
var image = new BitmapImage();

using (var stream = new FileStream(currentFile, FileMode.Open, FileAccess.Read))
{
    image.BeginInit();
    image.DecodePixelWidth = 100;
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.StreamSource = stream;
    image.EndInit();
}

image.Freeze();
CurrentImage.Source = image;

1
我会相信这个答案胜过我的,因为我已经有一段时间没有去弄清楚BitmapImage的怪癖了,而Clemens似乎对此还比较熟悉。 - Scott Chamberlain

5

在位图对象上调用.Freeze()方法,这会将其设置为只读状态并释放一些句柄,防止其被GC回收。

还有一件事情是,您可以告诉BitmapImage绕过缓存,您看到的内存可能来自缓存。

CurrentImage.Source = new BitmapImage(new Uri(currentFile), 
                                   new RequestCachePolicy(RequestCacheLevel.BypassCache));

最后,如果计算机上没有运行许多程序对系统施加内存压力,.net就可以等待需要进行垃圾回收(GC)的时间。进行GC是缓慢且会降低性能的,如果不需要进行GC因为没有人请求RAM,则不执行GC。

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