-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi-window memory leak #10029
Comments
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
Related with microsoft/microsoft-ui-xaml#7617 |
The same problem here, any workaround? |
I've been making some tests here, and the problem appears to be in a Window object that still forever allocated, in my tests, even I closed New Window and check if it removed from App.Current.Windows collection, the object never will be collect by GC and I cannot find who is locking up on the object tree. Unfortunately I didn't find any workaround for this issue, do you guys have any idea? |
Context: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. Note this doesn't fully solve dotnet#10029, as I still see something else holding onto `Window` in my example. This is WIP, as I'm interested in what my `WindowsDoNotLeak` test will do on all platforms.
Context: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. Note this doesn't fully solve dotnet#10029, as I still see something else holding onto `Window` in my example. This is WIP, as I'm interested in what my `WindowsDoNotLeak` test will do on all platforms.
Context: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. Note this doesn't fully solve dotnet#10029, as I still see something else holding onto `Window` in my example. This is WIP, as I'm interested in what my `WindowsDoNotLeak` test will do on all platforms.
Fixes: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well.
Fixes: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well.
Fixes: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. This is a perfect example of where using `ConditionalWeakTable` solves the issue: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well.
Fixes: dotnet#10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well.
Fixes: #10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: ```csharp void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } ``` It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well.
Fixes: #10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: ```csharp void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } ``` It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well. # Conflicts: # src/Controls/tests/Core.UnitTests/WindowsTests.cs
Fixes: #10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: ```csharp void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } ``` It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well. # Conflicts: # src/Controls/tests/Core.UnitTests/WindowsTests.cs
Fixes: #10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: ```csharp void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } ``` It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well. # Conflicts: # src/Controls/tests/Core.UnitTests/WindowsTests.cs
Fixes: #10029 I could reproduce a memory leak by doing: App.Current.OpenWindow(new Window { Page = new ContentPage() }); I found that `App.Current._requestedWindows` just held onto every `Window` object forever. Note that we *can't* use `ConditionalWeakTable` here: https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2 After initially trying this, I added a test showing not to use it. The problem is if the `string` is GC'd the `Window` will be lost. We can use `Dictionary<string, WeakReference<Window>>` instead. The only other change I made was to use `Guid.ToString("n")` as it removes `{`, `}`, and `-` characters from the string. After these changes, I still found `Window` objects hanging around that appeared to be held onto by many other objects. Then I reviewed: ```csharp void IWindow.Destroying() { SendWindowDisppearing(); Destroying?.Invoke(this, EventArgs.Empty); OnDestroying(); Application?.RemoveWindow(this); // This wasn't here! Handler?.DisconnectHandler(); } ``` It appears that upon `Window`'s closing, we didn't have any code that "disconnected" the MAUI handler. After this change, I could see `Window` objects properly go away. I added a unit test as well. # Conflicts: # src/Controls/tests/Core.UnitTests/WindowsTests.cs Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
Description
Closing a window results in a memory leak. The windows are never GC’d and often all their content is still visible within memory. Comparing this behavior with WinUI3 I have found that it works correctly in WinUI3, windows are created and recycled, counts go up, and back down. On MAUI the same process results in a steady increase in Window counts.
Steps to Reproduce
Expected Result:
Ref count should go down when a window is closed.
Actual Result:
Ref count keeps going up, window is never GC'd.
Link to public reproduction project repository
NA
Version with bug
Unknown/Other (please specify)
Last version that worked well
Unknown/Other
Affected platforms
Windows
Affected platform versions
All
Did you find any workaround?
No response
Relevant log output
No response
The text was updated successfully, but these errors were encountered: