Diagnosing Memory Management Issues

Duration

10 minutes

Goals

In this exercise, we will identify a memory leak in a Xamarin.Forms application which is being caused by an event handler. You can use the Xamarin Profiler (if you have it installed), or use the techniques outlined below to identify the issue.

Assets

The provided Exercise 2 folder contains a sub-folder named Start with a pre-built Xamarin.Forms application. You can use any of the platform head projects as your testing platform.

Challenge

We will first need to see if there is a leak in this application.

Once we've recognized there is a leak, we will begin to look for it based on our app behavior.

  1. What is the app doing when the leak occurs?
  2. What happens in the code which is executed when the leak happens?
  3. Verify that we've identified the leak using a finalizer.
  4. Fix the leak.
  5. Verify the changes.

Steps

Below are the step-by-step instructions to find and fix the memory leak.

Check for memory leaks in our app

Let's start by running our application and seeing if it is leaking memory.

  1. Open the starting NetworkState application from the Exercise 2 folder.
  2. Build the solution and then run the application in your platform of choice. Here we show it running in iOS:
  3. Click the GC button in the toolbar to force a GC - you should see output in the IDE. We are particularly interested in the major generation current size / previous size value:
  4. NetworkStateiOS[5553:1469657] info: GC_MAJOR: (user request) pause 5.79ms, total 5.78ms, bridge 0.00ms major 2944K/2288K los 61K/0K
    
  5. In Visual Studio for Windows, you will see this value shown graphically as an increasing line in the diagnostic view: (note that in the desktop apps it's a very slow increase which may be harder to spot - use Task Manager instead).
  6. Finally, for Windows UWP desktop apps, use Task Manager to watch the memory value:
  7. Play with the app - there's not much to do with it. Just click the "Show Network Status" button which navigates to a second screen. The screen background will be green when you are connected to a network and red if you are disconnected.
  8. Do this transition several times (back and forth between the screens). You should notice that your memory continues to grow. In the Xamarin apps, use the "GC" button to force a GC and look at the diagnostic output to see it growing. In Visual Studio, use the diagnostics window or Task Manager.

Identify the leak

We clearly have a memory leak when transitioning to the second screen.

  1. Open the ProblemPage.cs source file and examine the code - it's quite short. You can ignore the "buffers" section at the end, it's there to give the page a bit more bulk and make the leak easier to see since the ContentPage size is relatively small by default.
  2. Let's verify that the leak is a result of this page. Add a Debug.WriteLine to the constructor of the ProblemPage and output that the page has been created.
  3. Create a finalizer on the ProblemPage and output that the page is about to be collected. Make sure to surround your finalizer code in a #if DEBUG statement!
  4. Show Code

  5. Run the app again and switch back and forth between the pages. Does the problem page get collected? Force a GC by clicking the toolbar button - does it get collected now?

Fixing the leaking page

Recall from the lecture that delegates and events are a common source of live references which cause memory leaks in managed code. Notice that the constructor is wiring up an event handler to the CrossConnectivity.Current.ConnectivityChanged event. It turns out that this event is static under the covers - which means our pages are never going to be collected!

  1. We need to unwire from the event. The best place to do this in a Xamarin.Forms application is by overriding the OnDisappearing method. Do it there.
  2. Now, let's make sure this is properly paired - it's possible to create the page and reuse it in some cases, so we don't really want to wire up to the event handler in the constructor. Instead, move that registration into the OnAppearing override so it's properly matched with our un-wiring of the event.
  3. Run the app again and watch the debug window - does the page get collected now?

Summary

In this exercise, we have identified a problem in a Xamarin.Forms application due to an event handler tied to a static event. We corrected this by properly unregistering from the event handler. This is something you should always watch out for in your code - no matter what type of .NET application you are building!

Go Back