How to stop memory leak when popping pages

sandra 101 Reputation points
2021-01-18T14:37:16.17+00:00

I am trying to track down memory leaks in Xamarin forms (Android).

To illustrate the issue I have created the the sample project using Visual Studio 2019 and selecting the Flyout template for Android.
I have created a class to check for weak references as follows:

public static class Refs
{
    private static readonly List<WeakReference> _refs = new List<WeakReference>();

    public static readonly BindableProperty IsWatchedProperty = BindableProperty.CreateAttached("IsWatched", typeof(bool), typeof(Refs), false, propertyChanged: OnIsWatchedChanged);

    public static bool GetIsWatched(BindableObject obj)
    {
        return (bool)obj.GetValue(IsWatchedProperty);
    }

    public static void SetIsWatched(BindableObject obj, bool value)
    {
        obj.SetValue(IsWatchedProperty, value);
    }

    private static void OnIsWatchedChanged(BindableObject bindable, object oldValue, object newValue)
    {
        AddRef(bindable);
    }

    public static void AddRef(object p)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();

        GC.Collect();

        _refs.Add(new WeakReference(p));
        foreach (var ref1 in _refs)
        {
            if (ref1.IsAlive)
            {
                var obj = ref1.Target;
                Debug.WriteLine("IsAlive: " + obj.GetType().Name);
            }
            else
            {
                Debug.WriteLine("IsAlive: False");
            }
        }
        Debug.WriteLine("---------------");
    }
}

}
I added Refs.AddRef(this); in all views and viewmodel constructors.
To release the model for a new item I added the following code to NewItemPage.xaml.cs

protected void DisposeBindingContext()
{
if (BindingContext is IDisposable disposableBindingContext)
{
disposableBindingContext.Dispose();
BindingContext = null;
}
}
protected override void OnParentSet()
{
base.OnParentSet();

        if (Parent == null)
        {
            //Clear a bunch of bindings or dispose of ViewModel objects 
            BindingContext = null;
            this.DisposeBindingContext();

        }
    }

When run, output window shows the page is kept alive.
[0:] ---------------
01-18 14:11:55.696 I/art (22155): Starting a blocking GC Explicit
01-18 14:11:55.713 I/art (22155): Explicit concurrent mark sweep GC freed 95(3016B) AllocSpace objects, 2(64KB) LOS objects, 64% free, 4MB/12MB, paused 179us total 16.566ms
01-18 14:11:55.714 D/Mono (22155): GC_TAR_BRIDGE bridges 388 objects 450 opaque 1 colors 388 colors-bridged 388 colors-visible 388 xref 4 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.08ms tarjan 0.19ms scc-setup 0.10ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.08ms
01-18 14:11:55.714 D/Mono (22155): GC_BRIDGE: Complete, was running for 19.45ms
01-18 14:11:55.714 D/Mono (22155): GC_MAJOR: (user request) time 14.11ms, stw 15.03ms los size: 1024K in use: 173K
01-18 14:11:55.714 D/Mono (22155): GC_MAJOR_SWEEP: major size: 5280K in use: 3989K
01-18 14:11:55.732 I/art (22155): Starting a blocking GC Explicit
01-18 14:11:55.749 I/art (22155): Explicit concurrent mark sweep GC freed 3(72B) AllocSpace objects, 0(0B) LOS objects, 64% free, 4MB/12MB, paused 179us total 16.626ms
01-18 14:11:55.750 D/Mono (22155): GC_TAR_BRIDGE bridges 388 objects 450 opaque 1 colors 388 colors-bridged 388 colors-visible 388 xref 4 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.08ms tarjan 0.17ms scc-setup 0.13ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.08ms
01-18 14:11:55.750 D/Mono (22155): GC_BRIDGE: Complete, was running for 19.54ms
01-18 14:11:55.750 D/Mono (22155): GC_MAJOR: (user request) time 13.32ms, stw 15.13ms los size: 1024K in use: 173K
01-18 14:11:55.750 D/Mono (22155): GC_MAJOR_SWEEP: major size: 5280K in use: 3977K
[0:] IsAlive: AboutViewModel
[0:] IsAlive: AboutPage
[0:] IsAlive: ItemsViewModel
[0:] IsAlive: ItemsPage
[0:] IsAlive: False
[0:] IsAlive: NewItemPage
[0:] IsAlive: False
[0:] IsAlive: NewItemPage
[0:] IsAlive: False
[0:] IsAlive: NewItemPage
[0:] IsAlive: False
[0:] IsAlive: NewItemPage
[0:] IsAlive: NewItemViewModel
[0:] IsAlive: NewItemPage
[0:] ---------------
01-18 14:12:00.162 W/IInputConnectionWrapper(22155): finishComposingText on inactive InputConnection
01-18 14:12:01.684 W/IInputConnectionWrapper(22155): finishComposingText on inactive InputConnection
Thread finished: <Thread Pool> #7
The thread 0x7 has exited with code 0 (0x0).

Can anyone advise how the page can be freed from memory?

Developer technologies | .NET | Xamarin
{count} votes

Accepted answer
  1. sandra 101 Reputation points
    2021-01-20T10:05:50.147+00:00

    I have just found that enabling XAML Hot Reload keeps the pages in memory, so once disabled the pages are released.

    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.