Memory not getting released in WPF Image

2019-01-25 00:45发布

问题:

I am loading and unloading images in Canvas. I used the below code to load the Image.

Before loading my Image the memory consumption is 14.8MB.

Canvas c = new Canvas();

Image im = new Image();
ImageSource src = new BitmapImage(new Uri(@"E:Capture.png"));
im.Source = src;
im.Height = 800;
im.Width = 800;

c.Children.Add(im);
homegrid.Children.Add(c); //homegrid is my grid's name

The Image displayed correctly and the memory consumption now is 20.8MB. Then I unloaded the Image by the below code:

foreach (UIElement element in homegrid.Children)
{
    if (element is Canvas)
    {
        Canvas page = element as Canvas;

        if (page.Children.Count > 0)
        {
            for (int i = page.Children.Count - 1; i >= 0; i--)
            {
                if (page.Children[i] is Image)
                    (page.Children[i] as Image).Source = null;
                page.Children.RemoveAt(i);
            }
        }

        page.Children.Clear();
        page = null;
    }
}

homegrid.Children.RemoveAt(2);
InvalidateVisual();

The Image gets removed after this, but the memory is still 20.8 MB.

Can anyone help me out this?

回答1:

First of all you should test by explicitly invoking GC.Collect() to collect memory and see that memory releases or not because GC collection is indeterministic. You can't be sure that after your method execution GC runs and reclaim the memory.

So , at end put this code to explicitly force GC to run to check if actually memory is released or not:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

However, there is some known memory leak issues in BitmapImage creation which you can refer here, here and here.

Actually under the covers WPF keeps a strong reference between the static BitmapImage and the Image and hook some events on Bitmap image. So, you should freeze the bitmapImage before assigning to image. WPF doesn't hook events on freezed bitmapImage. Also set CacheOption to avoid any caching memory leak of bitmapImage.

Image im = new Image();    
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(@"E:Capture.png");
bi.EndInit();
bi.Freeze();
ImageSource src = bi;
im.Source = src;
im.Height = 800;
im.Width = 800;


回答2:

In .Net there is something called the garbage collector (GC) that is in charge of managing the memory you're using.

  • When you create an instance of an object, it requires some more memory.
  • When you remove your ImageSource from the Children collection, you don't actually free any memory, you just say "I don't want to use this instance anymore".

At this point the GC will help you. It'll automatically detect instances that are not used anymore, and will free the associated memory for you.

Do note it's an automatic process and you shouldn't (and you don't want to) take care of the memory management.

You can call GC.Collect(); to force the garbage collector to do its job right now, you'll see the memory will be released. NOTE: GC.Collect(); should be used in debug to detect memory leaks, but 99% of times you shouldn't call it explicitly in production code. GC.Collect(); is an operation that can use a lot of CPU time.