Category Archives: ViewModels

Memory Leak in Prism.Core (Xamarin.Forms)

Prism Version: 8.0.0.1909
Packages:
Prism.Unity.Forms
Prism.Forms
Prism.Core

A couple of weeks ago, we found a memory leak in one of our Xamarin Apps. Pages that get navigated to were still kept in memory, eventhough they have been removed from the NavigationStack. I tried to figure out why the pages are still being kept in memory, therefore I wrote a small program to recreate the issue (as usal I started out with the Prism Template for Xamarin Forms Apps):

  1. MainPage with MainPageViewModel – has a command to navigate to Page2 using Prism’s PageNavigationService
  2. Page2 (allocates 10MB data) and also has a ViewModel with 10MB memory allocation

Started the App, and navigated to Page2 – 9 times and back.

JetBrains dotMemory – first try

As you can see the memory consumption is going up all the time. When I trigger the Garbage Collector, some memory got freed.. But why? Easy as this – I didn’t bind the ViewModel to the View, so there was no retention path that holds the ViewModel back from being collected by the GC. The connection between the View and the ViewModel will be cut off by the Prism framework.

But what about the View – Why isn’t the View cleared from memory? Memory profiling is a little bit like searching a needle in the haystack. But JetBrains dotMemory is a great Tool and helps a lot. You can choose a specific instance, and you’ll find all objects that may be keeping it alive.

JetBrains dotMemory – Incoming References for Page2

Actually we would have to have a look at all of those references, one after the other. That’s very time consuming, and boring. But when we have closer look, we’ll see that Prism somehow looks very conspicious – the PageNavigationService, which implements INavigationService in Xamarin.Forms.

The reason why if has a reference to the page is, that it’s a PageNavigationService, that only works in connection to a specific page, so it isn’t strange that it keeps a reference to the page. But why is it still in memory? The only reference was used within the ViewModel, and that has been garbage collected. There has to be another reference, that’s why we need to have a look at the Prism Source Code, where the NavigationService is being created:

https://github.com/PrismLibrary/Prism/blob/master/src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs

public static class Navigation
{
        internal static readonly BindableProperty NavigationServiceProperty =
            BindableProperty.CreateAttached("NavigationService",
                typeof(INavigationService),
                typeof(Navigation),
                default(INavigationService));

        private static INavigationService CreateNavigationService(IScopedProvider scope, Page page)
        {
            var navService = scope.Resolve<INavigationService>();
            switch (navService)
            {
                case IPageAware pa when pa.Page is null:
                    pa.Page = page;
                    break;
                case IPageAware pa1 when pa1.Page != page:
                    return CreateNavigationService(ContainerLocator.Container.CreateScope(), page);
            }

            page.SetValue(NavigationScopeProperty, scope);
            scope.IsAttached = true;
            page.SetValue(NavigationServiceProperty, navService);

            return navService;
        }
}

I removed some lines of code, that are not imprtant. As you can see, the NavigationService is being attached to the page itself using an AttachedProperty. That’s a pretty cool solution, but the disadvantage is that AttachedProperties are nothing more than a big static Dictionary. The key of that Dictionary is the HashCode of the page (so this is a weak reference) and the value is the instance of the NavigationService (which in fact is being a static weak reference, including the BindingContext).

Update (2021/03/07) The reference of the attached property to the PageNavigationService actually is a (static) weak reference. Somehow the PageNavigationService is still bound, and the GC can’t free the memory.

A quick hack to solve that problem is to implement IDestructible within your PageViewModels (or ViewModelBase, when you use the Prism VS-template) and remove the Page-reference from the NavigationService:

public virtual void Destroy()
{
    if (NavigationService is IPageAware pageAware)
    {
        pageAware.Page = null;
    }
}

Do I really need to unsubscribe every event?

I’ve seen a lot of source code in my life. Some really good code, that I loved to read and understand. My wife loves to read good books, I love to read good source code. 🙂

But sometimes, you read lines of code and think: Why??? Why, is someone doing this??? I remember one code snipped, I’ve seen a couple of months ago. I think the developer was afraid of memory leaks, maybe he read an article about events causing memory leaks, when they are not detached.

To summon up the code, it was as simple as that:

A ViewModel_A published an event and ViewModel_B subscribed to it. The guy writing it (I think he tried to “optimize” it..) made both ViewModels IDisposable and ViewModel_B unsubscribed that event. It wasn’t quite clear who actually disposes the objects of ViewModel_B, but that’s another story.

But, do we really have to unsubscribe each event, we ever hook on? Short answer NO! It actually depends on your use case and the lifetime of each object. ViewModels usually have a short lifetime (as long as you are visiting the page), therefore you don’t normally have to destroy the subscription. (In regular settings, you actually don’t need events within ViewModels, but I will not say never)

So let’s have a look at our sample, with all available connections. We have a View, with ViewModel_A as the BindingContext. The View_A has a ListView with items, each item has ViewModel_B as the BindingContext. And each ViewModel subscribes to the UpdateEvent. (Ok, I made this one up, to be more logical, even though I would probably solve such a setting in a different way).

ModelConnections1

The Garbage Collectors looks through the references, and as long as View_A is somehow in a NavigationStack, or viewed on the screen, everything stays in memory. Let’s say our View_A gets disposed because the user closes the page (or whatever). Now we have lost all connections, and only the event will keep both ViewModels connected. But all of those ViewModels have no connection to a View anymore, so they are both dead, and the Garbage Collector will sooner or later clean them up.

But are there cases, when I need to tidy up on events?
Yes, definitely, there are some cases. You just have to keep one thing in mind:

The publisher keeps the subscriber alive. Not vice versa.

Let’s say we have a service, which is implemented as a singleton (or instantiated as such in an IOC container). So the object is alive over the whole app lifecycle. When this service publishes an event, and a ViewModel subscribes it, the ViewModel will never get destructed (perhaps sometime, when the app terminates). But beware, that just implementing an interface like IDisposable will not solve your problem .. you also need to use it and think of when you want to dispose of your object. Prism has a nice variant for PageViewModels; indestructible. When you use Prism, just have a look at it, it’ll help you with that decision.