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?
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:
after:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With