-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
[net7.0 / net8.0] [iOS] Memory leak when CollectionView.ItemsSource changes #14664
Comments
How is this different from #14654, which is not closed? |
@Nacompllo and you were testing this on .NET 7? It might be fixed by this one, which is only in main/.NET 8 currently: I will test your above sample, though, thanks. |
There appears to be an issue with images on iOS, a failing test: [Fact("Image Does Not Leak")]
public async Task DoesNotLeak()
{
SetupBuilder();
WeakReference reference = null;
await InvokeOnMainThreadAsync(async () =>
{
var layout = new VerticalStackLayout();
var image = new Image
{
Background = Colors.Black,
Source = "red.png",
};
layout.Add(image);
var handler = CreateHandler<LayoutHandler>(layout);
await image.Wait();
reference = new WeakReference(image.Handler.PlatformView);
});
await Task.Yield();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.NotNull(reference);
Assert.False(reference.IsAlive, "PlatformView should not be alive!");
} Still investigating what the fix would be. |
Context: dotnet#14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves dotnet#14664, but at least one part of it.
Context: dotnet#14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves dotnet#14664, but at least one part of it.
Context: dotnet#14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves dotnet#14664, but at least one part of it.
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS Related to: dotnet#14664 There are a couple cycles on iOS that causes memory leaks in all controls based on `ContentView`: * `Microsoft.Maui.Controls.ContentView` -> * `ContentViewHandler` -> * `Microsoft.Maui.Platform` -> * `CrossPlatformArrange/Measure` property -> * `Microsoft.Maui.Controls.ContentView` cycle To fix this, I made `CrossPlatformArrange/Measure` properties obsolete, using the weak `View` property directly instead. This applies to various other controls like `Border`, `RadioButton`, `SwipeItemView`, `SwipeView`. I did not yet fix `ScrollView` that appears to be more complicated to solve. For now, it supports both code paths. `ScrollView` will continue to have leaks in: * Closures with captured variables * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L195 * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L227 * `CrossPlatformArrange/Measure` * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L182-L186
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS Related to: dotnet#14664 There are a couple cycles on iOS that causes memory leaks in all controls based on `ContentView`: * `Microsoft.Maui.Controls.ContentView` -> * `ContentViewHandler` -> * `Microsoft.Maui.Platform` -> * `CrossPlatformArrange/Measure` property -> * `Microsoft.Maui.Controls.ContentView` cycle To fix this, I made `CrossPlatformArrange/Measure` properties obsolete, using the weak `View` property directly instead. This applies to various other controls like `Border`, `RadioButton`, `SwipeItemView`, `SwipeView`. I did not yet fix `ScrollView` that appears to be more complicated to solve. For now, it supports both code paths. `ScrollView` will continue to have leaks in: * Closures with captured variables * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L195 * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L227 * `CrossPlatformArrange/Measure` * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L182-L186
Hi everyone, I can see that the issue is still in an Open status, but I am writing to provide some feedback just in case it helps. And the result for the case of this incident is the same. There are still memory leaks when reproducing the issue. I share with you a youtube link to watch the video that shows it. https://www.youtube.com/watch?v=EAxcUQE6wLE Many thanks! |
Yes, this is not fixed until we get these merged: But then we'll need to retest the above sample app, to see if there are further issues. |
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS Related to: dotnet#14664 There are a couple cycles on iOS that causes memory leaks in all controls based on `ContentView`: * `Microsoft.Maui.Controls.ContentView` -> * `ContentViewHandler` -> * `Microsoft.Maui.Platform` -> * `CrossPlatformArrange/Measure` property -> * `Microsoft.Maui.Controls.ContentView` cycle To fix this, I made `CrossPlatformArrange/Measure` properties obsolete, using the weak `View` property directly instead. This applies to various other controls like `Border`, `RadioButton`, `SwipeItemView`, `SwipeView`. I did not yet fix `ScrollView` that appears to be more complicated to solve. For now, it supports both code paths. `ScrollView` will continue to have leaks in: * Closures with captured variables * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L195 * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L227 * `CrossPlatformArrange/Measure` * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L182-L186
Context: dotnet#14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves dotnet#14664, but at least one part of it.
Context: https://github.com/jonathanpeppers/MemoryLeaksOniOS Related to: dotnet#14664 There are a couple cycles on iOS that causes memory leaks in all controls based on `ContentView`: * `Microsoft.Maui.Controls.ContentView` -> * `ContentViewHandler` -> * `Microsoft.Maui.Platform` -> * `CrossPlatformArrange/Measure` property -> * `Microsoft.Maui.Controls.ContentView` cycle To fix this, I made `CrossPlatformArrange/Measure` properties obsolete, using the weak `View` property directly instead. This applies to various other controls like `Border`, `RadioButton`, `SwipeItemView`, `SwipeView`. I did not yet fix `ScrollView` that appears to be more complicated to solve. For now, it supports both code paths. `ScrollView` will continue to have leaks in: * Closures with captured variables * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L195 * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L227 * `CrossPlatformArrange/Measure` * https://github.com/dotnet/maui/blob/3b620952425ca3c4542c4632e05d09eb4f583e22/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs#L182-L186
Context: dotnet#14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves dotnet#14664, but at least one part of it.
* [ios] fix memory leak in Image Context: #14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves #14664, but at least one part of it. * Update src/Core/src/Platform/ImageSourcePartLoader.cs Co-authored-by: Matthew Leibowitz <mattleibow@live.com> * Update MauiImageView.cs * Update MauiImageView.cs * ISetImageHandler -> IImageSourcePartSetter * IImageHandler.OnWindowChanged() is now public * Fix IImageSourcePartSetter namespace * MauiImageView._handler can be readonly --------- Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
* [ios] fix memory leak in Image Context: #14664 (comment) `Image` has two different "cycles" on iOS: * `ImageHandler` -> `MauiImageView` -> `ImageHandler` * via the `WindowChanged` event * `ImageHandler` -> `ImageSourcePartLoader` -> `ImageHandler` * via `Handler`, `Func<IImageSourcePart?>`, and `Action<PlatformImage?>` This causes any `MauiImageView` to live forever. To solve these issues: * Get rid of the `MauiImageView.WindowChanged` event, and use a `WeakReference` to the handler instead. * `ImageSourcePartLoader` now only has a `WeakReference` to the handler. * This requires a new `ISetImageHandler` interface to be used by several handler types involving images. Hopefully the changes here are using `[Obsolete]` correctly to make things backwards compatible & less breaking changes. Unsure yet if this fully solves #14664, but at least one part of it. * Update src/Core/src/Platform/ImageSourcePartLoader.cs Co-authored-by: Matthew Leibowitz <mattleibow@live.com> * Update MauiImageView.cs * Update MauiImageView.cs * ISetImageHandler -> IImageSourcePartSetter * IImageHandler.OnWindowChanged() is now public * Fix IImageSourcePartSetter namespace * MauiImageView._handler can be readonly --------- Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
Context: #14664 Context: https://github.com/nacompllo/MemoryLeakEverywhere/tree/bugfix/memoryLeakItemsSource While looking at the customer sample, we found an issue with `BorderHandler`: * `ContentView` -> via `LayoutSubviewsChanged` * `BorderHandler` -> * `ContentView` Creating a cycle & memory leak on iOS and Catalyst. We could reproduce this in a device test. It appears the event only did this: void OnLayoutSubviewsChanged(object? sender, EventArgs e) { PlatformView?.UpdateMauiCALayer(); } And so instead, we can just call the extension method directly: this.UpdateMauiCALayer(); And the leak is gone! Co-authored-by: Haritha Mohan <harithamohan@microsoft.com>
I added some logging for the sample: * dotnet#14664 * https://github.com/nacompllo/MemoryLeakEverywhere/tree/bugfix/memoryLeakItemsSource I found the "logical children" grow indefinitely on iOS/Catalyst, after adding some logging it quickly got up to 71 items: 2023-07-06 09:04:27.745 MemoryLeakEverywhere[93127:7440192] Microsoft.Maui.Controls.CollectionView added child -> Microsoft.Maui.Controls.Image, count: 71 I was able to reproduce in a test, after I added some code to allow the `CollectionView` to create items (using `WidthRequest`/`HeightRequest`). When you replace an `ItemsSource` on iOS, in this way: var newCollection = new ObservableCollection<string>(); collectionView.ItemsSource = newCollection; foreach (var item in data) { newCollection.Add(item); } It appears that the old items are not removed, the test fails with: Expected: 3 Actual: 6 But passes on Windows & Android. Somewhere, we need to call one of: * `ItemsView.ClearLogicalChildren()` * `ItemsView.RemoveLogicalChild()` But I've not been successful yet.
The list of linked PRs above address various memory issues found the in above sample. However, the memory still appears to grow in the sample with dotnet/maui/main at this time. Latest IssueI found the "logical children" grow indefinitely on iOS/Catalyst, after adding some logging the sample above quickly got up to 71 items:
I can illustrate the issue in this test: It passes on Windows & Android, but on iOS & Catalyst the list of logical children grows. It appears to be due to this pattern: var newCollection = new ObservableCollection<string>();
collectionView.ItemsSource = newCollection;
foreach (var item in data)
{
newCollection.Add(item);
} If I change it to set var newCollection = new ObservableCollection<string>();
foreach (var item in data)
{
newCollection.Add(item);
}
collectionView.ItemsSource = newCollection; This version is faster anyway, but the issue seems to be related to the Workarounds for Above Sample
|
Hi @Nacompllo. We have added the "s/try-latest-version" label to this issue, which indicates that we'd like you to try and reproduce this issue on the latest available public version. This can happen because we think that this issue was fixed in a version that has just been released, or the information provided by you indicates that you might be working with an older version. You can install the latest version by installing the latest Visual Studio (Preview) with the .NET MAUI workload installed. If the issue still persists, please let us know with any additional details and ideally a reproduction project provided through a GitHub repository. 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 is a Memory leak issue, we tried to reproduce it with a user-provided project, and it took over an hour to run. But the memory |
Description
I am aware of the existence of the following bug #13530 but only mention is made of Windows, however in iOS the same memory leak problem also occurs when updating the ItemsSource of a CollectionView several times and the RAM memory is not released even by browsing backwards:
I leave a video playing an example repository where I can reproduce this problem in iOS until the application crashes due to lack of memory:
https://www.youtube.com/watch?v=_UTEGbE4-ug
I leave the repository that I show in the video so you can do the test:
https://github.com/nacompllo/MemoryLeakEverywhere/tree/bugfix/memoryLeakItemsSource
Steps to Reproduce
https://github.com/nacompllo/MemoryLeakEverywhere/tree/bugfix/memoryLeakItemsSource
Link to public reproduction project repository
https://github.com/nacompllo/MemoryLeakEverywhere/tree/bugfix/memoryLeakItemsSource
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
iOS
Affected platform versions
iOS 16
Did you find any workaround?
No response
Relevant log output
No response
Depends on
The text was updated successfully, but these errors were encountered: