-
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
[controls] fix memory leak in BindableLayout
#13550
[controls] fix memory leak in BindableLayout
#13550
Conversation
Context: dotnet#12130 Context: https://github.com/angelru/CvSlowJittering In reviewing the above sample, I found the following problem: * Setup a `BindableLayout` on a long-lived `BindableCollection<T>` * Navigate away, or otherwise remove the `BindableLayout` from the screen. * A `BindableLayoutController` object lives forever -- or as long as the `BindableCollection<T>`. Note that nothing ever removes/clears `BindableLayout.SetItemsSource()`. This gets really bad if you have a `BindableLayout` inside a `CollectionView`. In the sample above, it has a `<DataTemplate>` using `BindableLayout` inside. On Windows, I saw it creating infinite `BindableLayoutController` objects while scrolling. I could reproduce this issue in a unit test. I applied the same tricks from 0372df7 to solve the problem. dotnet#12130 is not fully solved as it still seems slow to me and still has a memory issue? Still thinking if there is a "comprehensive" way to solve all of these types of issues across the repo...
We'll re-evaluate this one for backporting next week once we know where things end up with some upcoming WeakReference changes. |
As seen in dotnet#13973, some of my recent changes had a flaw: * dotnet#13550 * dotnet#13806 * dotnet#13656 Because nothing held onto the `EventHandler` in some of these cases, at some point a GC will prevent future events from firing. So for example, my original attempt to test this behavior: [Fact] public async Task RectangleGeometrySubscribed() { var geometry = new RectangleGeometry(); var visual = new VisualElement { Clip = geometry }; bool fired = false; visual.PropertyChanged += (sender, e) => { if (e.PropertyName == nameof(VisualElement.Clip)) fired = true; }; // Was missing these three lines!!! // await Task.Yield(); // GC.Collect(); // GC.WaitForPendingFinalizers(); geometry.Rect = new Rect(1, 2, 3, 4); Assert.True(fired, "PropertyChanged did not fire!"); } In each case, I added an additional test showing the problem. I played around with some ideas, but the simplest solution is to save the `EventHandler` in a member field of the subscriber. Will keep thinking of smarter ways to handle this. I also fixed several GC-related tests that were ignored, hoping they might help find issues in this area. My `await Task.Yield()` trick was enough to make them pass.
Given the issue we found, I would say don't backport this yet: #13997 I'll remember to put |
As seen in #13973, some of my recent changes had a flaw: * #13550 * #13806 * #13656 Because nothing held onto the `EventHandler` in some of these cases, at some point a GC will prevent future events from firing. So for example, my original attempt to test this behavior: [Fact] public async Task RectangleGeometrySubscribed() { var geometry = new RectangleGeometry(); var visual = new VisualElement { Clip = geometry }; bool fired = false; visual.PropertyChanged += (sender, e) => { if (e.PropertyName == nameof(VisualElement.Clip)) fired = true; }; // Was missing these three lines!!! // await Task.Yield(); // GC.Collect(); // GC.WaitForPendingFinalizers(); geometry.Rect = new Rect(1, 2, 3, 4); Assert.True(fired, "PropertyChanged did not fire!"); } In each case, I added an additional test showing the problem. I played around with some ideas, but the simplest solution is to save the `EventHandler` in a member field of the subscriber. Will keep thinking of smarter ways to handle this. I also fixed several GC-related tests that were ignored, hoping they might help find issues in this area. My `await Task.Yield()` trick was enough to make them pass. * Fix tests in Release mode In `Release` mode, a `GC.KeepAlive()` call is needed for the tests to pass. Co-authored-by: GitHub Actions Autoformatter <[email protected]>
* Removed BuildTizenDefaultTemplate and just have it call RadioButton's default template since they were identical. (#13996) * Reinstate WebView cookie functionality for Android & iOS (#13736) * Fix iOS cookies * Fix Android Cookies * Update src/Core/src/Platform/iOS/MauiWKWebView.cs Co-authored-by: Manuel de la Pena <[email protected]> * Auto-format source code * Update MauiWKWebView.cs * Update src/Core/src/Platform/iOS/MauiWKWebView.cs --------- Co-authored-by: Manuel de la Pena <[email protected]> Co-authored-by: GitHub Actions Autoformatter <[email protected]> * Revert 10759. Fix Button sizing using HorizontalOptions. (#14005) * Ensure that Grid is treating star rows/columns as Auto when unconstrained (#13999) * Ensure that Grid is treating star rows/columns as Auto when unconstrained Fixes #13993 * Auto-format source code --------- Co-authored-by: GitHub Actions Autoformatter <[email protected]> * [iOS] Implement ScrollView Orientation (#13657) * [iOS] Remove not used mapper for ContentSize * [iOS] Implement Orientation mapping * [Samples] Add sample page for ScrollView orientation * Try without this * [iOS] Move from extension to helper * Add back removed API * Use SetNeedsLayout to call measure of ContentView * Cleanup * [Android] Fix Frame Renderer to use Wrapper View correctly (#12218) * [Android] Fix Frame to call missing mapper methods * - fix rebase * Auto-format source code * - update tests and wrapper view code * - remove code that's now generalized in ViewHandler * - cleanup frame renderer --------- Co-authored-by: GitHub Actions Autoformatter <[email protected]> * [controls] fix cases a GC causes events to not fire (#13997) As seen in #13973, some of my recent changes had a flaw: * #13550 * #13806 * #13656 Because nothing held onto the `EventHandler` in some of these cases, at some point a GC will prevent future events from firing. So for example, my original attempt to test this behavior: [Fact] public async Task RectangleGeometrySubscribed() { var geometry = new RectangleGeometry(); var visual = new VisualElement { Clip = geometry }; bool fired = false; visual.PropertyChanged += (sender, e) => { if (e.PropertyName == nameof(VisualElement.Clip)) fired = true; }; // Was missing these three lines!!! // await Task.Yield(); // GC.Collect(); // GC.WaitForPendingFinalizers(); geometry.Rect = new Rect(1, 2, 3, 4); Assert.True(fired, "PropertyChanged did not fire!"); } In each case, I added an additional test showing the problem. I played around with some ideas, but the simplest solution is to save the `EventHandler` in a member field of the subscriber. Will keep thinking of smarter ways to handle this. I also fixed several GC-related tests that were ignored, hoping they might help find issues in this area. My `await Task.Yield()` trick was enough to make them pass. * Fix tests in Release mode In `Release` mode, a `GC.KeepAlive()` call is needed for the tests to pass. Co-authored-by: GitHub Actions Autoformatter <[email protected]> * [iOS] Scroll with the keyboard to not block entries and editors (#13499) --------- Co-authored-by: dustin-wojciechowski <[email protected]> Co-authored-by: Gerald Versluis <[email protected]> Co-authored-by: Manuel de la Pena <[email protected]> Co-authored-by: GitHub Actions Autoformatter <[email protected]> Co-authored-by: Javier Suárez <[email protected]> Co-authored-by: E.Z. Hart <[email protected]> Co-authored-by: Rui Marinho <[email protected]> Co-authored-by: Shane Neuville <[email protected]> Co-authored-by: Jonathan Peppers <[email protected]> Co-authored-by: TJ Lambert <[email protected]>
I don't see whether this fix will be backported to .NET 7. |
Context: #12130
Context: https://github.com/angelru/CvSlowJittering
In reviewing the above sample, I found the following problem:
Setup a
BindableLayout
on a long-livedBindableCollection<T>
Navigate away, or otherwise remove the
BindableLayout
from the screen.A
BindableLayoutController
object lives forever -- or as long as theBindableCollection<T>
.Note that nothing ever removes/clears
BindableLayout.SetItemsSource()
.This gets really bad if you have a
BindableLayout
inside aCollectionView
. In the sample above, it has a<DataTemplate>
usingBindableLayout
inside. On Windows, I saw it creating infiniteBindableLayoutController
objects while scrolling.I could reproduce this issue in a unit test. I applied the same tricks from 0372df7 to solve the problem. #12130 is not fully solved as it still seems slow to me and still has a memory issue?
Still thinking if there is a "comprehensive" way to solve all of these types of issues across the repo...