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
- System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
- timer.Interval = TimeSpan.FromMilliseconds(1000d);
- timer.Tick += timer_Tick;
- timer.Start();
Then handle the tick:
Code Snippet
- void timer_Tick(object sender, EventArgs e)
- {
- long deviceTotalMemory = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“DeviceTotalMemory”);
- long applicationCurrentMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“ApplicationCurrentMemoryUsage”);
- long applicationPeakMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(“ApplicationPeakMemoryUsage”);
- System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString());
- System.Diagnostics.Debug.WriteLine(“Device Total : “ + deviceTotalMemory);
- System.Diagnostics.Debug.WriteLine(“App Current : “ + applicationCurrentMemoryUsage);
- System.Diagnostics.Debug.WriteLine(“App Peak : “ + applicationPeakMemoryUsage);
- }
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
- #if(DEBUG)
- /// <summary>
- /// Add a finalizer to check for memory leaks
- /// </summary>
- ~MyPage()
- {
- System.Diagnostics.Debug.WriteLine(“Finalizing “ + this.GetType().FullName);
- }
- #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
- GC.Collect();
- 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!
- public MyPage()
- {
- InitializeComponent();
- DispatcherTimer timer = new DispatcherTimer();
- timer.Interval = TimeSpan.FromSeconds(1);
- timer.Tick += (s, a) =>
- {
- this.txt.Text = “This causes a memory leak”;
- };
- timer.Start();
- }
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).