Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WriteableBitmap Memory Leak?

Tags:

i am using the code below to create a live tile, based on an UI element. It renders the uiElement on a WriteableBitmap, saves the bitmap + returns the filename. This method is run in a windows phone background task agent an i am running into memory limits.

private string CreateLiveTileImage(Canvas uiElement, int width, int heigth)
{
   var wbmp = new WriteableBitmap(width, heigth);
   try
   {
      wbmp.Render(uiElement, null);
      wbmp.Invalidate();

      var tileImageName = _liveTileStoreLocation;
      using (var stream = new IsolatedStorageFileStream(tileImageName, FileMode.Create, FileAccess.Write, IsolatedStorageFile.GetUserStoreForApplication()))
      {
         wbmp.SaveJpeg(stream, width, heigth, 0, 100);
         stream.Close();
      }

      uiElement = null;
      wbmp = null;
      GC.Collect();
      return "isostore:" + tileImageName;
   }
   catch (Exception exception)
   {
      // ...
   }
   return null;
}

I did some testing and the problem is: This methods leaks memory, but i do not know why/where?!

I also did some test runs - before first run into this method:

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
7.249.920 Bytes

This is ok, since the debugger is attached, which uses about 2 MB memory.

Doing some more runs of this method (set back to run method again in debugger):

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8851456  long +  40960
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8892416  long + 245760
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9138176  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9281536  long + 151552
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9433088  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9576448  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9715712  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9859072  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
10006528 long + 147456

So the memory, used by this method increases.

But why? In my opinion there are no references that prevent the objects from getting collected.

UPDATE on 04.05.2013

Hi,

thank you for all your answers! As suggested, i reduced the code + finnally was able to reproduce the problem in a few lines of code.

void Main()
{
   for (int i = 0; i < 100; i++)
   {
      CreateImage();
   }
}

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

private Rectangle CreateRectangle()
{
   var solidColorBrush = new SolidColorBrush(Colors.Blue);
   var rectangle = new Rectangle
   {
   Width = 1000,
   Height = 1000,
   Fill = solidColorBrush  // !!! THIS causes that the image writeableBitmap never gets garbage collected
   };

   return rectangle;
}

After starting the App: ApplicationCurrentMemoryUsage: "11 681 792 Bytes"

1 Iteration - ApplicationCurrentMemoryUsage: "28 090 368 Bytes"

5 Iterations - ApplicationCurrentMemoryUsage: "77 111 296 Bytes"

20 Iterations - ApplicationCurrentMemoryUsage: "260 378 624 Bytes"

After 23 Iterations: Out of Memory Exception. Ln.: var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

Only by commenting out the line "Fill = solidColorBrush", the CreateImage() method was called 100 times without any problems - after the 100th iteration, memory usage was about "16 064 512 Bytes".

So it seems the problem is the brush!! When used to fill an UI element, and later this UI elemnt is renderes on a writeable bitmap, the bitmap never gets garbage collected.

Of course this makes no sense in my opinion. The brush runns out of scope so it simply should be garbage collected too! (Setting the brush to null after it was used did not change anything)

Many of my UI elements use a brush for filling, so i cannot simply remove the usage of brushes. What do you think about this issue?

like image 264
Hannes Avatar asked Feb 05 '13 15:02

Hannes


1 Answers

The Problem is that rectangle.RenderTransform is an instance of an object and if you set writableBitmap to null the rectangle.RenderTransform Object is still alive and holds the rectangle in the memory... so the solution is to edit the code as follows:

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle.RenderTransform = null; //and your memory would be happy ;)
   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

see the memory screenshots...

before:

memory without setting rectangle.RenderTransform to null

after:

memory with setting rectangle.RenderTransform to null

like image 136
netsurfer.rs Avatar answered Nov 05 '22 21:11

netsurfer.rs