7

I need to dynamically load many (sometimes hundreds) of thumbnail images. For performance reasons I need to do this in a limited number of requests, I am using a single request/response for testing. I am sending the binary data for the images in the response and loading them into BitmapImage's using a MemoryStream. This works correctly until I load more than about 80 thumbnails, then I get the Catastrophic Failure exception. To make sure my data was not corrupt I tried loading a BitmapImage multiple times with the same byte array and it crashes after 80 or so loads.

Here is a sample of how the image is loaded from the byte array, the byte array is known to have valid image data (png):

private BitmapImage LoadImage(byte[] imageData)
{
    BitmapImage img = new BitmapImage();
    MemoryStream stream = new MemoryStream(imageData);
    img.SetSource(stream); // Exception thrown here after too many images loaded.
    return img;
}

I then use the BitmapImage as a source for an Image element on the page, but the error occurs in the img.SetSource(...) line above.

Adding GC.Collect() to the loop where I am loading thumbnail images lets me load a few more images, so I'm thinking this has something to do with memory management but I don't know what I can do to fix the problem.

toby
  • 885
  • 3
  • 10
  • 21
  • I'm not sure if this could be the problem but MemoryStream does have ReadTimeout and WriteTimeout properties. Could the stream be timing out? – Danexxtone Jul 08 '11 at 17:49
  • I tried setting ReadTimeout and got an exception: Timeouts are not supported on this stream. – toby Jul 08 '11 at 18:14
  • Can you clear up a few things: Why a byte array? Is there not a stream from the download that you could feed to `img.SetSource` directly? Are you sure the pngs being downloaded are of "thumbnail" size or are you downloading larger images that are being scaled by the image control? Are these thumbnails of photos? – AnthonyWJones Jul 08 '11 at 18:52
  • I am downloading multiple images in a single request, so I can't feed the request directly into `img.SetSource` (hence the byte array). The images are full size as I am caching them for the main viewing frame, but they are scanned documents and they are not very big (most are between 100kb and 200kb files). I would expect some sort of out of memory error if the issue is having too much data. – toby Jul 08 '11 at 19:24
  • Could you show more code related to the loading and processing itself? There's a few more questions and they might be answered faster by seeing the code. – Danexxtone Jul 08 '11 at 19:37

2 Answers2

6

I think quoting the answer providing by Microsoft in the above bug report is worthwhile since it is very succinct and descriptive of the problem as well as providing a recommended solution:

When Silverlight loads an image, the framework keeps a reference and caches the decoded image until flow control is returned to the UI thread dispatcher. When you load images in a tight loop like that, even though your application doesn't retain a reference, the GC can't free the image until we release our reference when flow control is returned.

After processing 20 or so images, you could stop and queue the next set using Dispatcher.BeginInvoke just to break up the work that is processed in one batch. This will allow us to free images that aren't retained by your application.

I understand with the current decode behavior it's not obvious that Silverlight is retaining these references, but changing the decoder design could impact other areas, so for now I recommend processing images like this in batches.

Now, if you're actually trying to load 500 images and retain them, you are still likely to run out of memory depending on image size. If you're dealing with a multi-page document, you may want to instead load pages on demand in the background and release them when out of view with a few pages of buffer so that at no point do you exceed reasonable texture memory limits.

Community
  • 1
  • 1
Xcalibur
  • 3,613
  • 2
  • 32
  • 26
0

I submitted a bug report with Microsoft for this issue: Catastrophic Failure exception thrown after loading too many BitmapImage objects from a Stream.

For now, I'm going to try working around this by using smaller image files for the thumbnails and/or not loading so many BitmapImages (unloading images when they are not in the viewable area and re-loading them when they come into view).

toby
  • 885
  • 3
  • 10
  • 21