.Net 8 MAUI Memory Leaks after navigating to multiple pages and then navigating to root page.
This issue has been moved from a ticket on Developer Community.
[severity:I'm unable to use this version] .Net 8 MAUI Memory Leaks after navigating to multiple pages and then navigating to root page.
We are seeing that due to memory leaks there is increase in memory usage each time we navigate from one page to another page and when we pop to root page then to the memory usage is not decreased. We have migrated app from Xamarin Forms to MAUI. This has impacted our app release plan. Any help from you would be appreciated.
Original Comments
Feedback Bot on 8/22/2024, 06:33 PM:
(private comment, text removed)
Original Solutions
(no solutions)
Hi I'm an AI powered bot that finds similar issues based off the issue title.
Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!
Closed similar issues:
- .net Maui: Memory Leak in iOS Controls not getting cleaned up (#22376), similarity score: 0.89
- Memory Leak on Page Navigation while removing the page. (#21403), similarity score: 0.81
- .NET MAUI Shell app page navigation memory leaks (#15930), similarity score: 0.80
- Memory leaks EVERYWHERE (#12039), similarity score: 0.78
- Memory leak while repeatedly navigating between pages (#10578), similarity score: 0.77
Note: You can give me feedback by thumbs upping or thumbs downing this comment.
We are having a very similar issue on our end, here is a link to that ticket as well: https://github.com/dotnet/maui/issues/24478
I was able to write a unit test in the MAUI source that reproduces the issue:
ShellNavigatingTests.cs:
[Fact]
public async void MemoryLeak()
{
Routing.RegisterRoute("page1/page2", typeof(DummyPage));
Routing.RegisterRoute("page1/page2/page3", typeof(DumberPage));
var shell = new TestShell(
CreateShellItem(shellSectionRoute: "page1")
);
int ctr = 0;
while (true)
{
await shell.GoToAsync("page1/page2/page3");
Assert.True(shell.CurrentPage is DumberPage);
await shell.GoToAsync("..");
Assert.True(shell.CurrentPage is DummyPage);
if (++ctr % 1000 == 0)
{
_testOutputHelper.WriteLine("Total memory: " + GC.GetTotalMemory(false).ToString("N0"));
}
}
}
class DummyPage : Page
{
}
class DumberPage : Page
{
}
After running this test for even 30 seconds you can see the memory usage goes up linearly. Currently investigating root cause.
After some debugging, I was able to find the following code which is causing a memory leak:
Page.cs
...
internal void OnAppearing(Action action)
{
if (_hasAppeared)
action();
else
{
EventHandler eventHandler = null;
eventHandler = (_, __) =>
{
this.Appearing -= eventHandler;
action();
};
this.Appearing += eventHandler;
}
}
Page.Appearing is not being unsubscribed properly:
Is this still happening in the latest releases? I am testing the code in main and I am not seeing these types of numbers (I got 3 max) and I am using this sample code: https://github.com/mattsetaro/MauiMemoryLeak/tree/main/MauiApp1
In that sample, I tried 8.0.3, 8.0.41 and 8.0.82 and was not really able to see any change. If I ran it as is, the size grew from 3 "mb" to 10 "mb" and then the GC collected it all. And, if I put this code in the OnAppearing that was logging, I see that it never changes even after running for a minute:
GC.Collect();
GC.WaitForPendingFinalizers();
await Task.Yield();
But, I was able to repro it in the test code:
public class ShellNavigatingTests : ShellTestBase
{
readonly ITestOutputHelper _testOutputHelper;
public ShellNavigatingTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public async Task MemoryLeak()
{
Routing.RegisterRoute("page1/page2", typeof(DummyPage));
Routing.RegisterRoute("page1/page2/page3", typeof(DumberPage));
var shell = new TestShell(
CreateShellItem(shellSectionRoute: "page1")
);
int ctr = 0;
while (ctr < 5_000)
{
GC.Collect();
GC.WaitForPendingFinalizers();
await Task.Yield();
await shell.GoToAsync("page1/page2/page3");
Assert.True(shell.CurrentPage is DumberPage);
await shell.GoToAsync("..");
Assert.True(shell.CurrentPage is DummyPage);
if (++ctr % 500 == 0)
{
_testOutputHelper.WriteLine("Total memory: " + GC.GetTotalMemory(false).ToString("N0"));
}
}
}
class DummyPage : Page
{
}
class DumberPage : Page
{
}
}
I can repro this issue at Windows platform on the latest 17.12.0 Preview 1.0(8.0.82 & 8.0.80). Sample project: MauiApp8 (1).zip
I can repro this issue at Windows platform on the latest 17.12.0 Preview 1.0(8.0.82 & 8.0.80). Sample project: MauiApp8 (1).zip
This sample isn't really correct.
This sample is pushing pages onto the stack so those pages aren't ever going to collect unless they are popped off the stack
for example, if I pop all the pages then the memory collects
Hi @vsfeedback. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.