WP7 Detecting Memory Leaks

In a WP7 application, if the app exceeds 90mb it is likely to be torn down without any exceptions. The app will appear to hang for a few seconds before the user is returned to the menu.

Finding memory leaks in a Silverlight app is a bit more challenging since you don’t have memory profiling tools like CLR Profiler which can be used for WPF.

Note the emulator does not currently enforce the same limit so your app may appear to work perfectly reliably until you finally get a device to test on.

Although the garbage collector does a good job, it will only clean up objects that are no longer reachable. With event subscriptions, timers and anonymous delegates it’s very easy to inadvertently make an object reachable. Here are the techniques I used to locate a memory leak.

1. Monitor the current and peak memory usage

See http://wotudo.net/blogs/wotudo/archive/2010/09/23/wp7-memory-monitoring-tip.aspx).

Setup a timer in your app.xaml like this (tip: you might want to wrap in #if (DEBUG)):           

Code Snippet
  1. System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
  2. timer.Interval = TimeSpan.FromMilliseconds(1000d);
  3. timer.Tick += timer_Tick;
  4. timer.Start();

Then handle the tick:

Code Snippet
  1. void timer_Tick(object sender, EventArgs e)
  2. {
  3.     long deviceTotalMemory = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“DeviceTotalMemory”);
  4.     long applicationCurrentMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“ApplicationCurrentMemoryUsage”);
  5.     long applicationPeakMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“ApplicationPeakMemoryUsage”);
  6.     System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString());
  7.     System.Diagnostics.Debug.WriteLine(“Device Total : “ + deviceTotalMemory);
  8.     System.Diagnostics.Debug.WriteLine(“App Current : “ + applicationCurrentMemoryUsage);
  9.     System.Diagnostics.Debug.WriteLine(“App Peak : “ + applicationPeakMemoryUsage);
  10. }

 

2. Add debug output to finalizers

Add this to each page and output when they are being finalized – you could add this to every class but perhaps the most important are the ones that can consume large amounts of memory such as pages with Panoramas and shouldn’t live for the lifetime of the app:

Code Snippet
  1. #if(DEBUG)
  2.         /// <summary>
  3.         /// Add a finalizer to check for memory leaks
  4.         /// </summary>
  5.         ~MyPage()
  6.         {
  7.             System.Diagnostics.Debug.WriteLine(“Finalizing “ + this.GetType().FullName);
  8.         }
  9. #endif

Navigate around your application, when you finally come to quit the app you shouldn’t see a finalizer being repeatedly called for an object that you assumed would be garbage collected. You can also call the following to force a collection, if you don’t see your object being collected then you may have a leak:

Code Snippet
  1. GC.Collect();
  2. GC.WaitForPendingFinalizers();

3. Event handlers

Check code for any event handlers that are not subscribing to their own events and ensure that you are unsubscribing from the event.

Watch out for anonymous event handlers that refer to members or variables in the parent scope/class (see modified closure) or subscriptions to events that don’t unsubscribe. For more information on this see:

http://blogs.msdn.com/b/ericlippert/archive/2007/06/06/fyi-c-and-vb-closures-are-per-scope.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2006/08/02/686456.aspx

4. Timers and DispatcherTimer

From MSDN: “A DispatcherTimer will keep an object alive whenever the object’s methods are bound to the timer”. Note the timer can only be garbage collected when disabled.

Bad code!
  1. public MyPage()
  2. {
  3.     InitializeComponent();
  4.     DispatcherTimer timer = new DispatcherTimer();
  5.     timer.Interval = TimeSpan.FromSeconds(1);
  6.     timer.Tick += (s, a) =>
  7.         {
  8.             this.txt.Text = “This causes a memory leak”;
  9.         };
  10.     timer.Start();
  11. }

 5. Consider using a WeakReference based Messaging approach

Such as the one in Laurent Bugnion’s MVVM Light Toolkit. This uses WeakReferences to ensure that subscribers to messages can still be garbage collected.

Finally

The issue I tracked down was to do with InteractionTriggers in MVVM Light, the Loaded event and static ViewModels which you can read about here. It’s worth keeping on top of current issues in whatever framework you’re using. For open Memory Leak issues in MVVM light, see here, it looks like they’re scheduled to be fixed in V4 (currently in beta).

Advertisements

4 thoughts on “WP7 Detecting Memory Leaks

  1. Hi,
    very useful post thanks. Have you noticed that the OS does not appear to garbage collect any pages in th eapp unless the back button is used? For example if you use the app bar or have in page navigation the views (and any associated view models in many cases) will not finalise until backed out or until the app is tombstoned or killed? Because of this you simply have to minimise the amount of data stored in your views/view models in many instances via the OnNavigatedFrom override to ensure memory doesn’t increase to rapidly

  2. to test this behaviour by the way i simply created a standard WP7 app in vs.net and created 2 pages with 2 buttons navigating between them. The pages are not finalised until the app exits. If you back from the 2nd to the 1st page instead of using an in page button, the page is finalised pretty quickly.

  3. Pingback: How to hunt Windows Phone 7 Memory Leak « vantsuyoshi

  4. Hi.

    You say that if I call

    1.GC.Collect();
    2.GC.WaitForPendingFinalizers();

    and I don’t see the Finalizers being called then I *may* have a memory leak. Is that really “may” or is it more definite than that? The reason I ask is because I’m calling this as part of my Main page, and seeing nothing until I actually exit the app, and then I see all of the finalizers being called.

    Worse than that, if I go Main > Page 1 > Main > Page 1 > Main > Page 1 > Main > Exit (through appropriate navigation and Back button), I don’t see the finalizer for Page 1 being called until the app exits, and then it gets called three times.

    I’m concerned that I do have memory leaks and I’m trying to figure out what is causing the objects to be held onto. I’ve tried to sort out handlers but that doesn’t seem to have fixed it.

    Thanks for any comments or suggestions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s