Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
328 views
in Technique[技术] by (71.8m points)

.net - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...