导航页面时,所有UWP应用程序是否都会泄漏内存?

Nig*_*elP 9 .net memory-leaks uwp windows-10-universal

所以我在最新版本的Windows 10上使用VS2017 v15.6.4开始使用UWP并在C#中开发一个简单的应用程序.

运行应用程序时,我注意到它的内存使用量会持续增加.

经过大量的代码配对后,我得出的结论是,这是由页面导航调用引起的,例如:

Frame.Navigate(typeof SomePage);
Frame.GoBack();
Frame.GoForward();
Run Code Online (Sandbox Code Playgroud)

创建和观察这个过程非常容易......

1)在VS2017中,创建一个新的空白应用程序(通用Windows)项目,将其命名为PageTest.

2)向项目添加一个新的空白页,命名为"NewPage".

3)将以下代码添加到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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

4)将以下(几乎相同的)代码添加到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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到这个简单的测试应用程序包含2个页面,当它运行时,应用程序将以每秒100次(通过计时器)的速率自动在两个页面之间导航,直到您关闭应用程序.

5)构建并运行应用程序.还要运行任务管理器并记下应用程序的初始内存占用量.

6)去喝杯咖啡.当你回来时,你会看到内存使用量增长了.它将继续增长.

现在我知道这个例子是不现实的,但它纯粹是为了证明我怀疑是影响大多数(如果不是全部)UWP应用的基本问题.

试试这个...

运行Windows 10设置应用程序(由Microsoft开发的UWP应用程序).再次,请注意它在任务管理器中的初始内存占用量.(在我的工具包上,这个起始大约12.1 MB).

然后反复单击系统设置图标...然后按返回按钮...然后系统设置图标...然后返回按钮...你明白了.并且观察内存占用也会增加.

几分钟后,我的MS设置应用程序内存消耗增加到超过90 MB.

此内存消耗似乎与UWP页面复杂性有关,如果您开始向页面添加大量XAML控件(尤其是图像控件),它会迅速上升.不久我的功能丰富的UWP应用程序消耗1-2GB内存.

所以这个"问题"似乎会影响所有基于框架的UWP应用程序.我已经在3台不同的PC上尝试了其他UWP应用程序,我看到了同样的问题.

使用我的功能丰富的应用程序,内存消耗已经非常糟糕,我现在正在考虑完全取消页面导航并将所有内容放在MainPage上.这不是一个愉快的想法.

无效的潜在解决方案......

我遇到过其他描述类似问题的文章,并且提出了我尝试过的解决方案,这些解决方案没有任何区别......

1)将以下任一行添加到.xaml页面定义中没有帮助...

NavigationCacheMode="Required" 

NavigationCacheMode="Enabled" 
Run Code Online (Sandbox Code Playgroud)

2)切换页面时手动强制垃圾收集没有帮助.所以做这样的事情没有任何区别......

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    GC.Collect();
}
Run Code Online (Sandbox Code Playgroud)

有谁知道是否有解决方案,或者它是UWP应用程序的基本问题?

Ste*_*SFT 5

在提供的repro代码中,您继续前进,这将创建页面实例的无限导航后退堆栈(请检查Frame.BackStack.Count)。由于这些实例存储在内存中,因此应用程序的内存使用量自然会不受限制地增长。

如果更改代码以导航回MainPage,因此将后退堆栈深度保持为2,则反复进行来回导航不会明显增加内存。

编辑 但是,如果我们在更长的时间内观察到这一点,则内存的增加是可测量的(在我的测试中,每个导航1.5KB)。自Windows 10 Update 1803起,这是平台代码中的已知漏洞尚未解决。

您在此处共享测试项目的更新版本:

这是NewPage.cs的代码:https ://1drv.ms/u/s!AovTw​​KUMywTNoYVFL7LzamkzwfuRfg

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果在真正的应用程序中,您仅在几个导航周期后就观察到MB大小的泄漏,则这不是由于上述平台错误,而是由于需要在特定应用程序中使用VS内存分析器进行其他调查例。通常,应用程序代码中的循环引用或静态事件处理程序可能导致泄漏。调试这些错误的一个很好的第一步是查看强制GC时页面的终结器是否按预期命中。如果不是,请使用VS内存分析工具来确定哪些对象正在泄漏以及谁在坚持。这些数据将有助于在特定情况下在应用代码中查明根本原因。通常,这是由于循环引用或未取消订阅静态事件处理程序造成的。如果您可以通过分析实际应用程序共享信息,很乐意为您提供更多帮助。