Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dispose of Image in WPF in Listbox (memory leak)

I have a ListBox with a bunch of images in it (done through a datatemplate). The images are created by setting the items source:

<Image x:Name="ItemImage" Source="{Binding ImageUrl}"/> 

and then they are cleared by using the listbox's Items.Clear() method. New Images are added by using the Items.Add method of the listbox.

However, memory usage just starts moving up and up and up. It is the same 300 or so small images that are getting displayed, but the memory never seems to get freed. The App starts using about 40Megs, and quickly climbs up to 700Megs. How do I free up the memory that all these images are using?

EDIT: One thing I forgot to mention, the images (which are about 4-5k each in size) are being loaded over the network. Is caching somehow responsible for this? Displaying 12 Images chews up about 10 Megs of memory, which is about 100X filesize.

like image 258
Kris Erickson Avatar asked Oct 09 '08 01:10

Kris Erickson


2 Answers

Unless you are doing anything unusual when loading the images (like using homebrewed image loaders or something) then the GC should wipe them out for you when nothing is referencing them anymore.

Are you holding on to references to the data anywhere? Remember that events and event handlers can sometimes "trick" the garbage collector into thinking that an object is still in use:

MyObject obj = new MyObject();
obj.TheEvent += new EventHandler(MyHandler);
obj = null;
// Now you might think that obj is set for collection but it 
// (probably - I don't have access to MS' .NET source code) isn't 
// since we're still listening to events from it.

Not sure if this applies to you, but at least that's were I'd check if I were you.

Also, if you have access to a profiler, such as AQTime or similar, then running your code through it might give you some hints.

You could also try and see if it makes any difference if you load images from disk or from resources embedded into your assembly.

like image 65
Isak Savo Avatar answered Oct 10 '22 07:10

Isak Savo


How about not using up all that memory in the first place?

(Note: The following paragraph and code are reproduced from this answer.)

Part of the problem is that it is loading the full image in each. You have to use an IValueConverter to open each image in a thumbnail size by setting either the DecodePixelWidth or DecodePixelHeight properties on the BitmapImage. Here's an example I use in one of my projects...

class PathToThumbnailConverter : IValueConverter {
    public int DecodeWidth {
        get;
        set;
    }

    public PathToThumbnailConverter() {
        DecodeWidth = 200;
    }

    public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
        var path = value as string;

        if ( !string.IsNullOrEmpty( path ) ) {

            FileInfo info = new FileInfo( path );

            if ( info.Exists && info.Length > 0 ) {
                BitmapImage bi = new BitmapImage();

                bi.BeginInit();
                bi.DecodePixelWidth = DecodeWidth;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.UriSource = new Uri( info.FullName );
                bi.EndInit();

                return bi;
            }
        }

        return null;
    }

    public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
        throw new NotImplementedException();
    }

}

You may also want to consider IsAsync=True in your Binding so that the converter is called on a background thread.

like image 43
Joel B Fant Avatar answered Oct 10 '22 08:10

Joel B Fant