2017-06-13 85 views
3

我有一个非常简单的WPF应用程序,它用于预览任何给定文件夹中的图像一次一个图像。您可以将其视为Windows映像查看器克隆。如果按下左箭头或右箭头键,则该应用程序具有用于加载文件夹中的上一个或下一个图像的PreviewKeyUp事件。防止内存膨胀当WPF中加载多个图像

<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; 
} 

如何在将多个图像加载到我的应用程序时防止内存膨胀?

+0

你也可以看看这个:https://stackoverflow.com/a/6271982/1136211 – Clemens

回答

5

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

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

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

最后,如果没有大量的对系统.NET的计算机将内存压力正在运行的程序是允许的,只要它想要的GC等。如果GC不是必需的,因为没有人请求内存条,那么GC不执行GC,因此执行GC时速度较慢并会降低GC期间的性能。

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; 
+1

我会信任在我的这个答案中,它已经有一段时间了,因为我必须弄清楚BitmapImage的怪癖,并且它在Clemens的想法中似乎更加新鲜。 –