In the course of wrapping up a SL-based WP7 application recently, I stumbled across a significant hurdle with memory consumption on the device...

Updated

After going back and forth with local Windows Phone guru Nick Randolph about the issue, and he blogged about the behaviour in more detail, we came to the conclusion that the garbage collection was not being triggered automatically. That's not necessarily a bad thing (see the conditions for triggering a garbage collection here) but is a concern for large WP7 applications.

The workaround I devised for this is to trigger a GC.Collect() after a number of page navigations. I would only add this to an application when absolutely necessary. The implementation of this I'll leave as an exercise to the reader.

Side note: I've seen various samples floating around which will use a timer to invoke a garbage collection call. Ignoring the use of timers on the device (which will drain the battery), a garbage collection call at an arbitrary time will likely impact on the user experience. Be smart about it, if you need to work this into an application.

Read on for the rest of the saga...

Before We Begin...

One of the requirements a WP7 application needs to satisfy is around memory consumption

5.2.5 Memory Consumption

An application must not exceed 90 MB of RAM usage, except on devices that have more than 256 MB of memory. You can use the DeviceExtendedProperties class to query the amount of memory that is available on the device and modify the application behavior at runtime to take advantage of additional memory. For more information, see the DeviceExtendedProperties class in MSDN.

Note:

The DeviceTotalMemory value returned by DeviceExtendedProperties indicates the physical RAM size in bytes. This value is less than the actual amount of device memory. For an application to pass certification, Microsoft recommends that the value returned by ApplicationPeakMemoryUsage is less than 90 MB when the DeviceTotalMemory is less than or equal to 256 MB

Source Microsoft

90 MB sounds like a lot of space - and yes, it is, when one remembers the era of 1.44MB diskettes (or earlier) you can't help but think that perhaps we are spoiled - but what can you do with that amount of memory?

Note - Disregard the "devices with > 256MB" exception mentioned, as I want to see how we can optimise memory usage on SL without sacrificing features.

How Do I Work Out My Memory Usage?

As mentioned above, the DeviceExtendedProperties class contains a lot of runtime information about the application.

I drop this method into the App.xaml.cs class so that I can get statistics at any point of the application's lifecycle.

public static void GetMemoryUsage(string task)
{
    var number = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
    Debug.WriteLine("{0} - ApplicationCurrentMemoryUsage: {1}", task, number);
    number = (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
    Debug.WriteLine("{0} - ApplicationPeakMemoryUsage: {1}", task, number);

    Debug.WriteLine("");
}

Oh, and don't forget the namespace

using Microsoft.Phone.Info;

This allows parts of the application to log diagnostics, like:

private void MainPageLoaded(object sender, RoutedEventArgs e)
{
    if (!App.ViewModel.IsDataLoaded)
        App.ViewModel.LoadData();

    App.GetMemoryUsage("Main - Loaded");

}

and see a message like in the Output Window:

Main - Loaded - ApplicationCurrentMemoryUsage: 39870464
Main - Loaded - ApplicationPeakMemoryUsage: 39899136

And now things get interesting...

Testing out a simple application - two screens, both use the Panorama Control and independent ViewModels which display a "large" list of items (270-ish items, but text only).

Selecting an item in the main screen will navigate to the second screen. Pressing back will return the application to the main screen.

The code is here and the sample output can be seen on Gist

Graphing the memory at each step, the graph looks like this:

But why is the memory footprint larger (by almost 10 MB) on returning to the main screen?

Note: Removing the Panorama Control did not change this behaviour - it just made the numbers smaller (a 5MB difference rather than 10MB). Just a sign that making screens leaner will certainly assist with reducing the overall footprint, but not the answer I was looking for.

Repeating the scenario a few times, and the behaviour is the same.

Puzzling...

And the adventure begins

The underlying why is what I want to understand more. Even with this simple application - which is displaying a large list of items - the size of the application is already close to the 90MB limit.

I've only been able to throw a couple of hours of spare time at this so far, but here's some notes from my investigation currently:

blog comments powered by Disqus