none
Memory Leak in BitmapImage and Image Controls?

    Question

  • Using Silverlight 1.1 Alpha, I can release memory of Image control.

    Following is the example for Alpha version.

    Image img = new Image();

    img.Source = new Uri(http://domain.com/big_image.jpg);

    img.Source = null;

     but it doesn't work in  SL 2.0 with BitmapImage and Image.

    Can I release memory of BitmapImage and Image controls in SL 2.0?

    Sunday, March 30, 2008 9:21 PM

Answers

  • Hello, you don't need to worry. Just set the Image's Source to null. When the BitmapImage and its underlying stream source is no longer referenced in you project, the CLR garbage collector will delete it, and call the proper unmanaged disposers.

    Tuesday, April 01, 2008 5:52 AM

All replies

  • Hello, you don't need to worry. Just set the Image's Source to null. When the BitmapImage and its underlying stream source is no longer referenced in you project, the CLR garbage collector will delete it, and call the proper unmanaged disposers.

    Tuesday, April 01, 2008 5:52 AM
  • Hi Yi-Lun,

     I made a silverlight 2.0 beta 1 project to test this. At the default page source I added following code:

       14 public partial class Page : UserControl

       15     {

       16         private Image TempImage = new Image();

       17 

       18         public Page()

       19         {  

       20 

       21             InitializeComponent();

       22 

       23             this.LayoutRoot.Children.Add(TempImage);

       24 

       25             this.MouseLeftButtonUp += new MouseButtonEventHandler(Page_MouseLeftButtonUp);

       26         }

       27 

       28         public void Page_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

       29         {

       30             for(int i=0; i<1000; i++)

       31             {

       32                 if (TempImage != null)

       33                 {

       34                     if (this.LayoutRoot.Children.Contains(this.TempImage))

       35                     {

       36                         this.LayoutRoot.Children.Remove(this.TempImage);                      

       37                     }

       38 

       39                     TempImage = null;

       40                 }

       41 

       42                 this.TempImage = new Image();

       43                 this.LayoutRoot.Children.Add(this.TempImage);               

       44             }

       45         }

       46     }

     Every time I click the Grid Control (this.LayoutRoot) the using memory increases. The Internet Explorer first uses about 20MB and after clicking the Grid with the mouse for a while and waiting for an hour, it uses about 100MB and doesn't goes down. Am I doing something wrong or is CLR garbage collector isn't working? Or is there another way? I tested this because my company project is using this kind of code everywhere.

    Monday, April 14, 2008 5:10 AM
  • It looks like you missed 2 "this." in front of "TempImage" in the second method.

     

    I would try that and see if the situation changes.

    Tuesday, April 22, 2008 4:57 PM
  • Logically "TempImage", "this.TempImage" is same because TempImage is a class member.

    Tuesday, April 22, 2008 7:40 PM
  • Normally yes. However there are cases (ASP.Net User Controls) where this is not the case and leaving out "this." will break it fun and joyously random ways.

    However with you using this.TempImage and TempImage just bugged me in terms of consistancy and would be the first thing I would check.

    I don't think I would have mentioned it if you did not use this.TempImage so much however.

    Tuesday, April 22, 2008 8:23 PM
  •  Hi,

    I have a similar issue in my application. I also think that the code  flyingmt provided is correct, so it shouldn't leak so much memory, does anyone as something to say about this matter? Any workarround?

     

    Any help will be much aprecciated,

     

    Thanks 

    Monday, June 02, 2008 6:28 AM
  • Hi,

    Since nobody had answered this problem, I sent this problem to MS.

     They say there inter version (Silverlight 2 Beta 2) has no problem with memory.

     But I prefer you to make a ObjectPool class, so the .net engine doesn't realloc the memory for new objects and don't delete unused objects..

     

    Monday, June 02, 2008 7:45 PM
  • Hi,

    I'm in the same situation. Flyingmt, should I understand that this issue will be solved in a next Beta2 or what?

    Thanks

    Tuesday, June 03, 2008 12:44 PM
  • Well, they said there next beta2 had no problem with my code. I think they didn't check it on beta1 because they only said "working version beta2".

    I don't know if it is solved on beta2 because it isn't released right now.

    But I think it will be same on beta2 so I prefer you to make an "Object Pool Class" that manages your Objects.

    You can think the "Object Pool Class" as your own garbage collector.

    It could be like this:

     public class ObjectPool<T>
     {
      Queue<T> _pool = new Queue<T>();
      public T Alloc()
      {
       T instance;
       if (_pool.Count == 0)
        instance = (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
       else
        instance = _pool.Dequeue();
       return instance;
      }
      public void Free(T instance)
      {
       _pool.Enqueue(instance);
      }
     }

    Wednesday, June 04, 2008 10:14 AM
  • I asked about it too, and I didn't get any official assurance at this point but it seems this week's Beta2 does not include any fix up for this issue. Hopefully the RTM will.

    Wednesday, June 04, 2008 10:28 AM
  • Me too.

    Wednesday, June 04, 2008 10:13 PM
  •  I think I am seeing this behavior, I'm witnessing massive memory leaks in Beta 2, and I don't think it is me. I hope Microsoft fixes it before the final release, because if they don't we will see third party binary patches for the Silverlight runtime, and I don't think anyone wants to go there.

    Wednesday, June 11, 2008 2:01 PM
  •  All Images are loaded via System.IO.Stream. Simply setting img.Source to null will not rid the stream automatically. You need to close and dispose of that stream and then set the stream and the image.Source to null. Doing this should release all that memory :)

    Sunday, June 15, 2008 12:54 PM
  • Setting an object to null does not "release" any memory. Once the object is out of scope and is no longer referenced, the Garbage Collector will clean it up and free the memory automatically.

    Sunday, June 15, 2008 4:22 PM
  •  Perhaps, but it's usually best to take care of your streams by closing them and disposing of them so you don't take up space in the Heap. Otherwise, you risk adding more up more and more memory until the GarbageCollector does get around to releasing it.

     

    Sunday, June 15, 2008 4:42 PM
  • Than what is wrong with this code? Am I doing something wrong here, because when I select images like this my app crashes with an "out of memory exception".

    OpenFileDialog ofd = new OpenFileDialog();

    ofd.Multiselect = true;

    ofd.Filter = "Image files selection (*.jpg)|*.jpg;";

    if (ofd.ShowDialog() == true)

    {

    foreach (FileDialogFileInfo file in ofd.SelectedFiles)

    {

    Stream stream = file.OpenRead();BitmapImage bitmapImage = new BitmapImage();

    bitmapImage.SetSource(stream);

     

    Image img = new Image();

    img.Source = bitmapImage;

    LayoutRoot.Children.Add(img);

     

    stream.Close();

    stream.Dispose();

    }

    }

    Monday, June 16, 2008 3:41 AM
  • // tomhat Image source settings are mostly used by Uri class. Even it uses stream inside, there is no way to access and dispose it. You have to leave it to the famework gc.

     

    // pbromberg Ya everybody knows that, but the problem is even it isn't referenced in the source gc will not clean it up and free the memory automatically asap.

    Thursday, June 19, 2008 7:38 PM
  •  we're running into the same issue. in a given session the user could potentially load a large number of images (at different times), but we can't seem to find a way to get the image memory usage free'd.

     does anyone have any new info?

    Thursday, October 23, 2008 3:48 AM
  • I solved the problem starting by detaching each and every event that i had appended to the "user control" that contained the image, and propagated this up, for every event attached between the controls that composed my application. I also removed the references between controls.

    This stopped my leaking problems for good.

    Best Regards,

    Joel Campos
    Thursday, October 23, 2008 4:30 AM
  •  this seems to fit exactly with what is described at this site (though they are talking about .net 3.5 WPF)

     http://wpfkorea.com/?document_srl=2655

     Scroll down to Item #7.

    Thursday, October 23, 2008 4:39 AM
  • ?Do you mean something like this:

     Stream stream  = f.OpenRead();
     Bitmap bitmap = new BitmapImage();
     bitmap.SetSource(stream);
     bitmap = null;
     stream.Close();
     GC.Collect();
     GC.WaitForPendingFinalizers();
     GC.Collect();

    .. or I did not get your suggestion? because I see no effect.

    Btw, this is my current workaround for this situation: I have a low res image resource file that I set as source to the Image controls that I want to clear. Being very small it does not require memory. 

     

    Thursday, October 23, 2008 10:37 AM
  • Here's another workaround.  I needed this specifically for loading images from a URL, rather than the local site.

    One issue will be determining the height/width of the image if you're using this method.  I was lucky in that all of the image dimensions I was concerned with are known.

    The trick is to use a Canvas with its background set to an ImageBrush.  Apparently the clean up is happening as desired here.  I took a shot in the dark and it seems to work.  This is as of only an hour ago, so if there are hidden issues here that I'm missing, please flag them!!

    namespace Test
      {
      public class FakeImage : Canvas
        {
        // image data
          ImageBrush m_brush = null;
          BitmapImage m_bmp = null;
        // image load event handler
          EventHandler<DownloadProgressEventArgs> m_eventHandler = null;
     
        public FakeImage(string url)
          {
          // generate source items
            m_bmp = new BitmapImage();
            m_brush = new ImageBrush();
          // dimensions set into canvas
            this.Width = 256;
            this.Height = 256;
          // Download event handler
            m_eventHandler = new EventHandler<DownloadProgressEventArgs>(m_bmp_DownloadProgress);
            m_bmp.DownloadProgress += m_eventHandler;
          // trigger it
            Reset(url);
          }
        public void Reset(string url)
          {
          // set the url
            m_bmp.UriSource = new Uri(url);
            m_brush.ImageSource = m_bmp;
            this.Background = m_brush;
          }
        void m_bmp_DownloadProgress(object sender, DownloadProgressEventArgs e)
          {
          if (e.Progress == 100)
            {
            // clear so secondary download is blocked (when canvas refreshes state???)
              m_bmp.UriSource = null;
            }
          }
        }
    }
     
    Thursday, December 04, 2008 4:25 PM
  •  thanks dude, your 3-liner fixed my memory issue too.

    Sunday, August 02, 2009 6:48 PM