5

My program has a lot of small images (Image controls are small, not the images themselves) and by saying a lot I mean more than 500. These images are generated asynchronously and then assigned to the Image controls, which were initialized before.
Basically my code does the following:

            filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("{0}.JPG", Guid.NewGuid().GetHashCode().ToString("x2")));
            converter.ConvertPdfPageToImage(filename, i);
            //Fire the ThumbnailCreated event
            onThumbnailCreated(filename, (i - 1));  

There is no memory leak in code that creates the images, I have the following code:

            string[] files = Directory.GetFiles("C:\\Users\\Daniel\\Pictures", "*.jpg");
            for(int i=0; i<files.Length; i++){
                onThumbnailCreated(files[i], i);
            } 

Still the problem persists. This happens in the event handler method:

    void Thumbnails_ThumbnailCreated(ThumbnailCreatedEventArgs e, object sender)
    {
        //Since we generate the images async, we need to use Invoke
        this.parent.Dispatcher.Invoke(new SetImageDelegate(SetImage), e.Filename, e.PageNumber);
    }

    private void SetImage(string filename, int pageNumber)
    {
        BitmapImage bitmap = new BitmapImage();
        bitmap.BeginInit();
        //I am trying to make the Image control use as less memory as possible
        //so I prevent caching
        bitmap.CacheOption = BitmapCacheOption.None;
        bitmap.UriSource = new Uri(filename);
        bitmap.EndInit();
        //We set the bitmap as the source for the Image control
        //and show it to the user
        this.images[pageNumber].Source = bitmap;
    }

With 468 images the program uses about 1Gb of memory and then runs out of it at all. Is my task even possible to achieve using WPF or is the number of images too high? Maybe there is something wrong with my code?
Thanks in advance

user7116
  • 63,008
  • 17
  • 141
  • 172
Cracker
  • 912
  • 2
  • 14
  • 26
  • Are you using wpf on framework 3.5 without sp1? – Rafal Nov 28 '12 at 18:06
  • @Rafal .NET Framework 4.0. Not sure about the SP. – Cracker Nov 28 '12 at 18:07
  • You say small images what is average resolution? – Rafal Nov 28 '12 at 18:10
  • @Rafal By saying small images I mean the size of the `Image` controls. The images itself are pretty large, something like 700x1000. – Cracker Nov 28 '12 at 18:11
  • You know that whole image is loaded regardless of size of a control. I suggest to reduce size of those images or create a second version with significantly smaller resolution for this purpose. – Rafal Nov 28 '12 at 18:13
  • 2
    Why dont you want to use BitmapCacheOption.OnLoad? – Lonli-Lokli Nov 28 '12 at 18:20
  • @Lonli-Lokli Because if I set it to `OnLoad` it would cache the entire image and store it in the memory. Otherwise all requests for the image are filled directly by the image file. – Cracker Nov 28 '12 at 18:25
  • I'm having this problem. Loading 18 BitmapImages puts me at 1GB of memory. Is there a way to only load the image in memory when it is displayed? – AndrewRalon Jul 24 '15 at 16:54

3 Answers3

4

You should freeze these images and set their width (or height) to that will be actually used in the application if possible:

// ...
bitmap.DecodePixelWidth = 64; // "displayed" width, this improves memory usage
bitmap.EndInit();

bitmap.Freeze();
this.images[pageNumber].Source = bitmap;
user7116
  • 63,008
  • 17
  • 141
  • 172
  • Do you know their actual size being used in the app, as in are these images larger than the thumbnail will be displayed? – user7116 Nov 28 '12 at 18:08
  • The images are larger than the thumbnails displayed. – Cracker Nov 28 '12 at 18:09
  • Ah, WPF will keep the entire image data in memory (plus overhead) unless you tell it during creation to throw that info away using `DecodePixelWidth` (or `DecodePixelHeight`, but not both). – user7116 Nov 28 '12 at 18:13
  • Much better now. Thanks a lot! Still takes up 254 MiB for 400 images, though. Is this the best possible result? – Cracker Nov 28 '12 at 18:23
  • Probably, are all 400 displayed at the same time? You should look into other cache options or virtualization if these elements are in an `ItemsControl`. – user7116 Nov 28 '12 at 18:24
-4

Try this:

private void SetImage(string filename, int pageNumber)
{
    using (BitmapImage bitmap = new BitmapImage())
    {
        bitmap.BeginInit();
        //I am trying to make the Image control use as less memory as possible
        //so I prevent caching
        bitmap.CacheOption = BitmapCacheOption.None;
        bitmap.UriSource = new Uri(filename);
        bitmap.EndInit();
        this.images[pageNumber].Source = bitmap;
    }
}

That will dispose of your bitmaps when you're done with them.

Ben Walker
  • 2,037
  • 5
  • 34
  • 56
  • 3
    Clearly he is using the bitmap, because he is assigning it to `images`. Calling `Dispose` may indeed help down the line, but certainly not in the spot you've placed it. – Cory Nelson Nov 28 '12 at 18:00
  • @CoryNelson you are exactly right, I need the images to be shown during the whole program runtime. – Cracker Nov 28 '12 at 18:06
-4

It may be the event handlers that are causing your memory leaks.

See this SO question:

Why and How to avoid Event Handler memory leaks?

Community
  • 1
  • 1
Shai Cohen
  • 6,074
  • 4
  • 31
  • 54
  • @sixlettervariables: Why very unlikely? And unless the answer in **wrong**, why the downvote? If the OP is hooking up event handlers and not disposing of them properly, there is a very good chance that can cause memory leaks. My answer was a valid suggestion. – Shai Cohen Nov 28 '12 at 19:19
  • I don't see any code which is wrong regarding event handlers. It is also not the cause of the OP's problem at hand. – user7116 Nov 28 '12 at 19:37
  • @sixlettervariables: The fact that this was not the problem the OP was experiencing is irrelevant. My suggestion was still valid and plausible. I would appreciate if you reverse the downvote. Thank you. – Shai Cohen Nov 28 '12 at 21:35