Memory leak on UWP with target versions 10.0.17763.0, 10.0.18362.0 and 10.0.19041.0

Daniele 1,996 Reputation points
2020-09-29T09:46:08.877+00:00

In an UWP application when I set TargetPlatformVersion and TargetPlatformMinVersion to 10.0.17763.0, 10.0.18362.0 or 10.0.19041.0 (I keep them always aligned) I found that pages are not released from memory causing serious issues, as the application memory usage quickly grows up.

This GitHub repo contains the code that reproduce the problem https://github.com/DanieleScipioni/MemoryLeak, this a basic application with 2 pages. You can just go from the first page to the second, the second page contains a ListView of ListViews with an ItemClick event handler.

<Page x:Class="MemoryLeak.Views.ListsPage">  
    <Grid>  
        <ListView IsItemClickEnabled="True"  
                  CanReorderItems="False"  
                  ItemsSource="{x:Bind _secondPageViewModel.List, Mode=OneWay}">  
            <ListView.ItemsPanel>  
                <ItemsPanelTemplate>  
                    <ItemsStackPanel Orientation="Horizontal" />  
                </ItemsPanelTemplate>  
            </ListView.ItemsPanel>  
            <ListView.ItemTemplate>  
                <DataTemplate x:DataType="local:StringList">  
                    <ListView ItemsSource="{x:Bind}"  
                              ItemClick="ListView_OnItemClick"  
                              IsItemClickEnabled="True"/>  
                </DataTemplate>  
            </ListView.ItemTemplate>  
        </ListView>  
    </Grid>  
</Page>  

To see the memory leak is enough to go forward and back from the 2 pages, and see memory increasing on the Task Managed, anyway I used Visual Studio Diagnostic Tool.

With Visual Studio Diagnostic Tool I execute this test: open app, go to next page, go back and take a snapshot, again go to next page, go back and take a another snapshot, then stop data collection and compare the 2 snapshots. GC is started several time before of each snapshot.

With TargetPlatformVersion 10.0.19041.0, there are 205 more objects in the second snapshot

29103-2004-with-eventhandler-overview.png

and looking at count diff and at paths to root I see this

29151-2004-with-eventhandler-paths-to-root.png

So at the end ListsPage is not released from memory.

The behavior must be that both count and count diff is 0, meaning that ListsPage is released from memory.

Universal Windows Platform (UWP)
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Richard Zhang-MSFT 6,936 Reputation points
    2020-09-29T10:28:29.907+00:00

    Hello,

    Welcome to Microsoft Q&A.

    When we use Frame.Navigate(typeof(SomePage)), by default, a new instance is generated based on the type of this Page. So multiple navigations actually creates multiple instances.

    When the page is navigated, the previous page will not be released, because for the Frame, a history record is required, which will retain a reference to the page

    If we want to limit this situation, we can cache the page:

    private bool _isLoaded = false;  
    public ListsPage()  
    {  
        _secondPageViewModel = new SecondPageViewModel();  
        NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;  
        InitializeComponent();  
        Loaded += async (sender, args) =>  
        {  
            if (!_isLoaded)  
            {  
                _isLoaded = true;  
                await _secondPageViewModel.Init();  
            }  
        };  
    }  
    

    After the page is cached, navigate to the page again, and Frame will use the cache first. This means that no new page instances will be created, and the memory usage will not increase.

    ---

    Update

    In your question, you did not use Frame for page navigation (sorry, this is my oversight).

    Even so, caching pages is still a solution to the problem.

    Simply put, every time you call App.NavigateTo(Type), you recreate the instance of a page based on this Type.

    Every time a ListsPage is created, a List<StringList> will be regenerated, and the memory overhead is mainly used to create the corresponding ListView.

    So as an alternative to caching, we can create a FrameworkElement list. When the page is first created, the page will be stored in the list. The next time we navigate, we can check the list and take out our previous cache instead of creating a new one.

       private List<FrameworkElement> _pageList;  
         
       public App()  
       {  
           _pageStack = new Stack<Type>();  
           _pageList = new List<FrameworkElement>();  
           InitializeComponent();  
       }  
         
       //...  
         
       private void ChangeWindowContent(Type type)  
       {  
           if (_pageList.Any(p => p.GetType().Equals(type)))  
               Window.Current.Content = _pageList.Where(p => p.GetType().Equals(type)).First();  
           else  
           {  
               var page= (FrameworkElement)Activator.CreateInstance(type);  
               _pageList.Add(page);  
               Window.Current.Content = page;  
           }  
             
         
           SystemNavigationManager systemNavigationManager = SystemNavigationManager.GetForCurrentView();  
           systemNavigationManager.AppViewBackButtonVisibility = CanGoBack  
               ? AppViewBackButtonVisibility.Visible  
               : AppViewBackButtonVisibility.Disabled;  
         
       }  
    

    In this way, by calling the previous cache, we can avoid the additional overhead caused by repeatedly creating instances.

    In ListsPage, please use the previous reply to modify the Loaded event to avoid repeated creation of List<StringList>

    Thanks.


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Cliff 1 Reputation point MVP
    2021-01-13T18:09:55.16+00:00

    Hi All,

    I am seeing the same issue but from a slightly different angle, I am using a Xamarin.Forms project that has a UWP target and in this project the Xamarin CollectionView builds on the UWP ListView so when I update the Binding Object of the ListView as needed for the project every 5 seconds there is a massive memory leak.

    SO this isn't a page navigation/build issue it's in the way that the ListView is building the ViewCell as it looks like it is not reusing but creating new each time and thus the resources are not release.

    0 comments No comments

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.