uno icon indicating copy to clipboard operation
uno copied to clipboard

Memory Leaks when using Frame navigation in WPF

Open Jalroka opened this issue 4 years ago • 9 comments

This was done using Visual Studio 2022 Preview 4.1 and Uno 3.10.11

The issue can be observed by running the performance profiler and navigation back and forth between the pages. After about 40 navigations memory usage starts to go up by about 1mb per transition. This bug seems to require the DataContext to be set like this.

<Page.DataContext>
    <vm:MainPageViewModel x:Name="VmContext"/>
</Page.DataContext>

The View Models I'm using are only empty classes but yet they are not being disposed. If I null out the datacontext in the code behind I can keep them from remaining on the stack when there's nothing actually bound. However even when doing that I can see the memory usage continue to rise so there's still something else.

If I run this code without setting the DataContext I don't see the memory leak.

Any feedback?

NavigationMemoryLeakTest.zip

Jalroka avatar Oct 05 '21 20:10 Jalroka

Thanks for the report. Does the page leak if the DataContext is set through the codebehind ?

jeromelaban avatar Oct 06 '21 12:10 jeromelaban

Yes it still leaks, though the memory consumption appears to increase at a slower pace. I had to perform closer to 80 navigations before I started to see significant increases in memory consumption. When I set the data context in the code behind and then filtered to look specifically for view models I was only seeing viewmodels for page 1 even though I was navigating between main page, page 1, and page 2.

Jalroka avatar Oct 09 '21 04:10 Jalroka

Adding this to the navigate back in the page 1 code behind does clear out the ViewModels. However it does not stop the overall memory leakage.

    private void ToMainClick(object sender, RoutedEventArgs e)
    {
        Frame.GoBack();
        this.SuspendBindings();
        this.DataContext = null;
    }

Jalroka avatar Oct 09 '21 04:10 Jalroka

I've taken a look at troubleshooting this during a live coding: https://youtu.be/oS9XuCqCBzI.

It seems that it's a caching behavior hiding a internal propagation issue.

Can you try with your project to see if FrameworkTemplatePool.IsPoolingEnabled = false; help? Also, can you simply try waiting 30 seconds to see if the memory is reclaimed?

jeromelaban avatar Oct 12 '21 20:10 jeromelaban

I recently tried updating that same sample project as posted before to the latest release version of Platform Uno. The same issue still appears to exist.

Jalroka avatar Feb 11 '22 20:02 Jalroka

Hi.

I might have ran into this problem. Is there any updates? I have a lot of data in my models and navigating to some views increases memory ~100mb per view transition.

It looks like multiple instances of the same views are created. Changing a value triggeres Propertychaned one extra time for each revisit of a view. Added a modified repro where this is visible.

Any possible workarounds? :)

NavigationMemoryLeakTest.zip

maaxe avatar Sep 02 '22 13:09 maaxe

@maaxe are you able to do some memory profiling to find out what's leaking?

jeromelaban avatar Sep 02 '22 14:09 jeromelaban

@jeromelaban I've made some attempts, but im not really sure whats expected to be there and not. What I've seen though is multiple instances of the page and "Page_Bindings" (the ViewModels stays at one instance). I also found FrameworkTemplatePool similar to you im your livecoding, however setting FrameworkTemplatePool.IsPoolingEnabled = false; did not help.

maaxe avatar Sep 05 '22 06:09 maaxe

@jeromelaban FYI: I noticed that the modified repro i provided was using old Uno packages, but the behavior is the same after updating to latest packages (4.4.20).

maaxe avatar Sep 08 '22 07:09 maaxe

@jeromelaban FYI: Updated the repro to the latest 4.7 release, still leaking! Is there some timeline for when you guys might look in to this or is there some workaround? It's really hurting performance after several navigations.

maaxe avatar Jan 30 '23 07:01 maaxe

We're making iterative improvements to the memory consumption, and your specific scenario is related to the reuse of ListView items. There's no timeline we can provide at this time, but if you need more information contact us at [email protected].

jeromelaban avatar Jan 30 '23 19:01 jeromelaban

Recent dev builds of Uno contain additional fixes that improve the situation for this issue.

Note that the behavior of Frame is impacted by the FrameworkTemplatePool.IsPoolingEnabled behavior (it is enabled by default), where the content of a Page can be reused automatically, causing explicit DataContext to be kept alive until the FrameworkTemplatePool is effectively automatically cleared (about every minute based on usage).

If you don't want to experience these temporary memory retentions (UI creation performance benefits from the FrameworkTemplatePool feature), it's best to set and remove the DataContext in the OnNavigatedTo/OnNavigatedFrom.

jeromelaban avatar Feb 09 '23 19:02 jeromelaban

App1.zip

@jeromelaban Tested with hte latest 4.8.0-dev.290 and unfortunately the issue is still there. I've tested with both disableing FrameworkTemplatePool and removing DataContext. But the memory is never reclaimed and keeps on growing!

I attached a repro. Every navigation back to Page1 generates one extra properychanged event when editing the value on Page1, I guess it's the old pageinstances that still holds a binding to the property (?).

maaxe avatar Feb 10 '23 07:02 maaxe

@maaxe Can you explain how you're using the repro sample? Which buttons, in which order?

jeromelaban avatar Feb 10 '23 12:02 jeromelaban

@jeromelaban Sorry for the unclear description! Here is the step by step:

  1. Run WPF head
  2. Navigate to Page 1. Edit the value in the textbox. Check output where I added debugprints everytime the property is fetched.
  3. Navigate to Page 2.
  4. Repeat step 2 and 3 and notice that the property is fetched one extra time for each navigation.

Taking snapshots of the memory usage also shows that each instance of Page1 is still in memory and not released.

I should also mention that FrameworkTemplatePool.IsPoolingEnabled; is set to false in the repro

maaxe avatar Feb 10 '23 12:02 maaxe

Thanks. This is then an expected behavior of Frame. Navigating forward between Page1 and Page2 will keep the reference to pages in the BackStack. If you do want to clear the stack and release previous instances, use the BackStack.Clear() method.

Note that WinUI by default only keeps a backstack of the types, not the references to instances of pages. This still creates a leak as the BackStack may grow indefinitely. The Uno behavior is a performance optimization that may change in the future (https://github.com/unoplatform/uno/issues/4156).

If you're only navigating to single pages from your NavigationView, you may want to use a ContentControl instead, and swap view instances explicitly in your NavigationView_OnItemInvoked method instead of using Frame navigation.

jeromelaban avatar Feb 10 '23 13:02 jeromelaban