在WPF中加载多个图像时防止内存膨胀

时间:2017-06-13 21:25:44

标签: c# .net wpf image

我有一个非常简单的WPF应用程序,用于一次在一个图像中预览任何给定文件夹中的图像。您可以将其视为Windows Image Viewer克隆。该应用程序有一个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;
}

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

2 个答案:

答案 0 :(得分:5)

在位图对象上调用.Freeze(),将其设置为只读状态并释放其上的一些句柄,以防止它获取GC。

你可以做的另一件事是你可以告诉BitmapImage绕过缓存,你看到的内存可以来自缓存。

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

最后,如果计算机上没有运行很多程序,会给系统带来内存压力。只要它想要GC,就可以等待.net。在GC期间执行GC很慢并降低性能,如果不需要GC,因为没有人请求ram,那么它不会执行GC。

答案 1 :(得分:3)

当您说预览时,您可能不需要完整的图像尺寸。因此,除了调用Freeze之外,您还可以设置BitmapImage的DecodePixelWidthDecodePixelHeight属性。

我还建议直接从FileStream而不是Uri加载图片。请注意,UriCachePolicy的在线文档说它是

  

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

所以它可能不适用于本地文件Uris。

为了安全起见,你可以这样做:

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;