Prevent memory spatter when loading multiple images in WPF

I have a very simple WPF application that is used to preview images in any folder one image at a time. You can think of it as a clone of Windows Image Viewer. The app has a PreviewKeyUp event used to load the previous or next image into the folder if the left or right arrow is pressed.

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

      

The problem I'm trying to solve is that when loading multiple images, there are a lot of memory bloats going on until garbage collection occurs. You can see this in the screenshot that I used to use the application memory. It's not uncommon for it to exceed 300MB before garbage collection.

screenshot

I tried wrapping the image in a using statement, but that doesn't work because BitmapImage doesn't implement IDisposable.

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

      

What can I do to prevent memory bloat when loading multiple images into my application?

+3


source to share


2 answers


Call .Freeze()

on a bitmap object, this sets it to read-only state and frees up some of its handles that are preventing it from getting GC'ed.

Another thing you can do is tell BitmapImage to bypass the cache, the memory you see might be from the cache.



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

      

Finally, unless a lot of programs are running on the computer putting pressure on the system .net memory is allowed to wait as long as the GC wants. GC execution is slower and degrades performance during GC, if GC is not required because no one is asking for RAM then it does not perform GC.

+5


source


When you say the preview, you probably don't want the full image size. So, in addition to calling Freeze

, you can also set the BitmapImage DecodePixelWidth

or DecodePixelHeight

.

I would also recommend loading images directly from FileStream

instead Uri

. Please note that the online doc UriCachePolicy

says it

... a value that represents the caching policy for images coming from an HTTP source.



so that it doesn't work with the local Uris file.

To be on the safe side, you can do this:

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;

      

+3


source







All Articles