Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do all UWP apps leak memory when navigating pages?

So I've been getting my teeth into UWP and developing a simple app in C# using VS2017 v15.6.4, on the latest release of Windows 10.

When running the app I notice its memory usage continues to increase over time.

After a lot of pairing back of the code, I've come to the conclusion that this is being caused by page navigation calls, such as:

Frame.Navigate(typeof SomePage);
Frame.GoBack();
Frame.GoForward();

It is very easy to create and observe this process...

1) In VS2017, create a new Blank App (Universal Windows) project, call it PageTest.

2) Add a new Blank Page to the project, naming it 'NewPage'.

3) Add the following code to the MainPage.xaml.cs:

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace PageTest
{
    public sealed partial class MainPage : Page
    {
        DispatcherTimer timer = new DispatcherTimer();

        public MainPage()
        {
            InitializeComponent();
            timer.Interval = TimeSpan.FromSeconds(.01);
            timer.Tick += Timer_Tick;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            timer.Start();
        }

        private void Timer_Tick(object sender, object e)
        {
            timer.Stop();
            Frame.Navigate(typeof(NewPage));
        }
    }
}

4) Add the following (almost identical) code to NewPage.xaml.cs:

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace PageTest
{
    public sealed partial class NewPage : Page
    {
        DispatcherTimer timer = new DispatcherTimer();

        public NewPage()
        {
            InitializeComponent();
            timer.Interval = TimeSpan.FromSeconds(.01);
            timer.Tick += Timer_Tick;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            timer.Start();
        }

        private void Timer_Tick(object sender, object e)
        {
            timer.Stop();
            Frame.Navigate(typeof(MainPage));
        }
    }
}

You can see that this simple test app contains 2 pages, and when it runs, the app will automatically navigate between the two pages at the rate of 100 times per second (via the timers) until you close the app.

5) Build and run the app. Also run Task Manager and note the app's initial memory footprint.

6) Go and make a cup of coffee. When you come back you'll see the memory usage has grown. And it will continue to grow.

Now I know this example is unrealistic, but it is here purely to demonstrate what I suspect is a fundamental problem affecting most (if not all) UWP apps.

Try this...

Run the Windows 10 Settings app (a UWP app developed by Microsoft). Again, note it's initial memory footprint in Task Manager. (On my kit this starts at about 12.1 MB).

Then repeatedly click on the System Settings icon... then the Back button... then the System Settings icon... then the Back button... You get the idea. And watch the memory footprint also increase.

After a few minutes of doing this, my MS Settings app memory consumption went up to over 90 MB.

This memory consumption seems to be related to UWP page complexity and it goes up rapidly if you start adding a lot of XAML controls to your pages, especially Image controls. And it doesn't take long before my feature rich UWP app is consuming 1-2GB memory.

So this 'problem' seems to affect all frame based UWP apps. I've tried it with other UWP apps on 3 different PC's and I see the same problem on them all.

With my feature rich app, memory consumption has got so bad that I'm now considering scrapping Page navigation altogether and putting everything on the MainPage. Which is not a pleasant thought.

Potential Solutions That Don't Work...

I've come across other articles describing a similar issue and there are proposed solutions that I've tried, which don't make any difference...

1) Adding either of the following lines to the .xaml page definitions does not help...

NavigationCacheMode="Required" 

NavigationCacheMode="Enabled" 

2) Manually forcing garbage collection when switching pages does not help. So doing something like this makes no difference...

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    GC.Collect();
}

Does anyone know if there a solution to this, or is it a fundamental issue with UWP apps?

like image 734
NigelP Avatar asked Apr 14 '18 13:04

NigelP


3 Answers

In the repro code provided you keep navigating forward, which will create an infinite navigation backstack of page instances (check Frame.BackStack.Count). Since those instances are stored in memory the app's memory usage will naturally grow unbound.

If you change the code to navigate back to MainPage, and therefore keep the backstack depth at 2, the memory will not grow noticeably with repeated back and forth navigation.

EDIT However, if we observe this over a longer period of time there is a measurable increase of memory (1.5KB per navigation in my testing). This is a known leak in platform code that has not yet been addressed as of Windows 10 Update 1803.

The updated version of your test project is shared here:

Here is the code for NewPage.cs: https://1drv.ms/u/s!AovTwKUMywTNoYVFL7LzamkzwfuRfg

public sealed partial class NewPage : Page
{
    DispatcherTimer timer = new DispatcherTimer();

    public NewPage()
    {
        InitializeComponent();
        timer.Interval = TimeSpan.FromSeconds(.01);
        timer.Tick += Timer_Tick;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        timer.Start();
        long managedMem = GC.GetTotalMemory(true);
        ulong totalMem = Windows.System.MemoryManager.AppMemoryUsage;
        System.Diagnostics.Debug.WriteLine(string.Format("Managed Memory: {0} / Total Memory: {1}", managedMem, totalMem));
    }

    private void Timer_Tick(object sender, object e)
    {
        timer.Stop();
        Frame.GoBack();
    }
}

If in your real app you observe a MB size leak after just a couple of navigation cycles then this is not due to above mentioned platform bug, but due to something else that needs to be investigated in the specific app, with the VS memory profiler for example. Often times leaks can be caused due to circular references or static event handlers in the app code. A good first step to debug these would be see if the page's finalizer gets hit as expected when forcing a GC. if not, use the VS memory profiling tools to identify what objects are being leaked and who is holding on to that. That data will help to pinpoint the root cause in the app code for that specific case. Typically those are due to circular references, or static event handlers not being unsubscribed. Happy to help more with this if you can share info from profiling the actual app.

like image 121
Stefan Wick MSFT Avatar answered Oct 29 '22 20:10

Stefan Wick MSFT


Yes. This is a bug in UWP. I opened a microsoft support ticket a few month ago and they said last week that they found the bug and solved them. They will ship the fix with the windows insider preview build with the next updates (so i think next week - on the 21.12.2018 it still wasn't included). the fix for everybody will be shipped with the spring update for windows 10.

like image 41
Dosihris Avatar answered Oct 29 '22 20:10

Dosihris


We made huge improvements in memory handeling and GC calls with latest UWP 6.2.9 Nuget update while targeting >=RS3 (Win 10 1709). The complete release notes are here

like image 33
ClaudiuC_MS Avatar answered Oct 29 '22 21:10

ClaudiuC_MS