Forms not clearing (and so leaking) UI elements - e.g. Labels and BindablePropertyContext - on Pop

JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭

We're experiencing memory leaks in our Xamarin.Forms Android application, and I've been investigating using Profiler. This has led me to running Profiler against a very basic Forms app.

The very basic Forms app is based on the simple List->Detail template project which is available in Visual Studio For Mac.

All it does is navigate from a list page to a details page using the inbuilt navigation stack.

    async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
    {
        var item = args.SelectedItem as Item;
        if (item == null)
            return;

        await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));

        // Manually deselect item
        ItemsListView.SelectedItem = null;
    }

Going from List->Details->List 11 times and then taking a snapshot in Profiler reveals that we've got 372 BindablePropertyContext objects and 22 Labels (among other objects) created and not released:

I have tried setting Content to null and BindingContext to null in the OnDisappearing event of the Details page, and also putting an explicit GC.Collect() in the OnAppearing of the List page. Neither of these fix the problem.

This is not acceptable for an app which is intended to run continuously on a device!

So is there a core memory leak in Forms, or am I missing something?

I'm using Xamarin.Forms 2.5.0.121934

Best Answer

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    There are always two garbage collectors going: The .NET GC and the platform (Android) GC.
    .NET GC can't collect something until Android GC releases the object, and Android GC is notorious for holding on to things for a while.

    So my question is... If you aren't looking for instantaneous GC... do the objects eventually release and get collected? Perhaps if you continue to consume memory for legitimate purposes like taking/loading photos since they will eat memory quickly... can you see the old objects get collected as your need for memory becomes more immediate?

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭

    @ClintStLaurent Thanks - that's what I was thinking too. I'm not looking for instantaneous GC, but for confirmation that we haven't got a fundamental leak in Forms.

    I'll put some code in to consume memory (as you suggested), and see if these UI artefacts are GC'd as appropriate.

  • NMackayNMackay GBInsider, University mod

    Certainly in my MVVM Lights app, when a page is popped of the stack I set it's binding context to null, I noticed that seems to help release resources quicker.

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭

    @NMackay Yes, we're doing that in our main app. My concern is that there is still a low-level leak in Forms, which is not acceptable to my customer.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @JamesLavery said:
    Running this on a less powerful device (i.e. with less memory) showed

    Great technique for testing/debugging/confirming!

  • @NMackay This is a basic problem I have with releasing memory in mobile apps. How do you know when the page is done? OnDisappearing gets called for BOTH when you hit back (this should mean the page is done and can release its resources) but ALSO when push a new page on the stack since the previous page IS disappearing. So you don't want to release a page's resources in OnDisappearing since it could be for two different reasons. What does everybody else do?

  • NMackayNMackay GBInsider, University mod
    edited March 2018

    @ShawnCastrianni.5092 said:
    @NMackay This is a basic problem I have with releasing memory in mobile apps. How do you know when the page is done? OnDisappearing gets called for BOTH when you hit back (this should mean the page is done and can release its resources) but ALSO when push a new page on the stack since the previous page IS disappearing. So you don't want to release a page's resources in OnDisappearing since it could be for two different reasons. What does everybody else do?

    Safest way is to handle cleanup is in the the Popped event, it's easier if the page is wrapped in a navigation page, OnDisappearing is not safe as you suggested as I've seen it fire in the past showing the dialler etc, firing intents etc.

    using Xamarin.Forms;
    using XamFormsTestApp.Data.Services.Common;
    
    namespace XamFormsTestApp.CustomPages
    {
        public class CustomNavigationPage : NavigationPage
        {
    
            public CustomNavigationPage(Page content)
                : base(content)
            {
                Init();
            }
    
            private void Init()
            {
                Popped += (sender, e) =>
                {
                    var navpage = e.Page as IPageLifetime;
                    if (navpage != null)
                    {
                        // Unregister vm of page, message listener etc
                        navpage.CleanupPage();
                    }
                    e.Page.BindingContext = null;
                };
            }
        }
    }
    
    
  • @NMackay Awesome! Thanks. I will use this technique.

  • @JamesLavery I am not sure about your conclusion. I am using Xamarin.Forms 2.3.3.193 and my app crashes due to memory leak. When I run it in the profiler, I believe the profiler does a GC when you take a snapshot. My snapshot still shows 171,364 BindablePropertyContext objects that are live. I will try the latest Xamarin.Forms as maybe they did have a leak but it was fixed. Anybody know if XF used to have a leak regarding BindablePropertyContext objects?

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭

    @ShawnCastrianni.5092 Although that's a lot of BindablePropertyContext objects, are you sure that it's these which are consuming the memory?

    What memory does profiler show them as holding (only the live ones of course). I've never seen a crash from a leak due to BindablePropertyContext objects.

    Check with profiler that there isn't something else leaking.

    How soon (ie after how many operations) does the app crash?

    I'd also recommend upgrading to the latest Forms if you can.

  • @JamesLavery @NMackay I am trying to find my memory leaks. I implemented @NMackay and can see my pages being freed when I pop them. However, I have a SyncFusion SfDataGrid which does seem to be freed, however, the custom cell I use for that SfDataGrid is not being freed. Can either of you help me to understand this path to root? I have 6,821 BoxViews in memory for SfDataGrid that don't exist anymore. These might be why I still have so many BindablePropertyContexts in memory. I am not sure what this path to root is telling me. Why does it stop at BoxRenderer? What is keeping the BoxRenderer in memory?

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭
    First I'd suggest contacting Syncfusion support, in case it's a known bug in their control.

    The PropertyChangedEventHandler looks suspicious. Have you unsubscribed all event handlers for the control? I suspect that this is internal to the control but it's still checking your code.
  • HarikrishnanHarikrishnan USMember ✭✭

    Hi @ShawnCastrianni.5092

    Thank you for using Syncfusion Products.

    I have checked your use case "the custom cell used in template columns are not being freed". We generally use to ensure memory for our every release. Am not sure what you have coded in your template. We have planned to check our memory stuff again now. I have also noticed that you have created a support ticket for our Direct Trac support incidents regarding the same. Our support team will look into your problem with the utmost priority and provide you a solution there.

    Thanks,
    Harikrishnan

    Note: I work for Syncfusion.

Sign In or Register to comment.