scribble
Feb 8 2014

Avoiding memory "leaks" in Windows Phone page navigation

The nice thing about programming with managed languages such as C# or Java is that we don't need to worry about memory leaks. After all, we have a nice butler called the garbage collector to take care of things for us. Or at least, we usually don't. It's still possible to get memory "leaks".

I'm writing "leak" in quotation marks as they are not exactly the same as your classical C-style leaks, where a block of memory has been allocated but there is no pointer pointing to that address anymore. Rather, unless your code is calling a native component (this might happen if you're using a 3rd party-library which uses native code for speed, such as Nokia's Imaging SDK) the opposite is often quite true : you have a pointer to a block of memory that you don't actually needed and because this pointer exists, the garbage collector can't get rid of it.

Here's an example of something that happened in my recent developer ventures. Navigating back and forth between two pages would give me an OutOfMemoryException. What would be the cause?

The first step is to measure memory usage. This page describes techniques to do so better than I could in a blog post, but the jist of it is that you either use Visual Studio's memory profiler or the static variables

DeviceStatus.ApplicationCurrentMemoryUsage
DeviceStatus.ApplicationPeakMemoryUsage
DeviceStatus.ApplicationMemoryUsageLimit

Finding the exact place where the memory leak occurs can be tricky, especially if the memory being leaked occurs in small pieces throughout the code. In that case, finding one place where the memory leaks is still good enough of a starting point. A way to do this is to add a watch in the visual studio debugger for DeviceStatus.ApplicationCurrentMemoryUsage that you can monitor as you step through your code.

In my case, the memory usage increased significantly after the following lines :

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // ...

    NavigationData.OriginalImage = NavigationData.OriginalImage.Rotate(
        360 - NavigationData.RotationAngle);
    ImageVertical.Source = NavigationData.OriginalImage;

    // ...
}

It's kind of obvious that creating a new image would involve a few megabytes - that doesn't indicate that it involves a memory leak. However, since I am replacing the original image with the rotating image, I would expect the original image to be freed. However, in managed languages, memory does not get freed the moment it is no longer used, so we need to force a call to the garbage collector.

NavigationData.OriginalImage = NavigationData.OriginalImage.Rotate(
    360 - NavigationData.RotationAngle);
ImageVertical.Source = NavigationData.OriginalImage;
GC.Collect();

Calling the garbage collector does not free memory - then it means that we still have a pointer to the original image somewhere. Where? Just to confirm that the leak isn't due to a faulty implementation of WriteableBitmap.Rotate, which comes from the third-party extension WriteableBitmapEx, or Image.Source, I run that code inside a loop.

for (int i = 0; i < 100; i++)
{
    NavigationData.OriginalImage = NavigationData.OriginalImage.Rotate(
        360 - NavigationData.RotationAngle);
    ImageVertical.Source = NavigationData.OriginalImage;
    GC.Collect();
}

Peak memory increases, but no leak.

Since I don't use NavigationData.OriginalImage anywhere else in code that gets reached at this point, then it must have something to do with page navigation.

Placing a breakpoint in the constructor of the page, I notice the constructor gets called every time the page is navigated to. If the old page were to remain alive, then ImageVertical.Source would still hold a pointer to the original images.

And that's indeed where the problem lies. Using :

NavigationService.Navigate(new Uri(page, UriKind.Relative));

Creates new pages. Why aren't the old pages destroyed? Because WP keeps them in a history of pages as to support the back button. Thus, in my case, if I replace one call of NavigationService.Navigate with

NavigationService.GoBack();

then the old pages get removed from the page history stack and no memory leak occurs. That's a solution if pages follow a clear hierarchical order First Page -> Second Page -> Third Page -> ..., but what if the app is implemented in such as way to all pages can navigate between each other and there is not clear "first" page, ala finite state machine?

Then it becomes important to remove stack frames every time navigation occurs.

if (App.RootFrame.CanGoBack)
    App.RootFrame.RemoveBackEntry();
NavigationService.Navigate(...);

In practice most apps do follow a hierarchical structure as alternatives are highly likely to break guidelines regarding the use of the back button but if the developer requires strict control over page navigation, then it's important to remove unused stack via RemoveBackEntry.

Garbage collection is nice, but it's no free lunch!

In this blog post, I described only one way memory "leaks" can occur. I've added links below to other possible causes, as explained by other writers.
http://sartorialsolutions.wordpress.com/2010/10/15/wp7-detecting-memory-leaks/
http://suchan.cz/2013/11/how-to-debug-most-common-memory-leaks-on-wp8/
http://blogs.codes-sources.com/kookiz/archive/2013/02/17/wpdev-memory-leak-with-bitmapimage.aspx
http://blogs.msdn.com/b/tess/archive/2006/01/23/net-memory-leak-case-study-the-event-handlers-that-made-the-memory-baloon.aspx

scribble