dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.21k stars 1.75k forks source link

Rising Memory Consumption leading to crashing App after a while when Utilizing NotifyCollectionChangedAction.Reset in ObservableRangeCollection #21015

Open MAUIoxo opened 8 months ago

MAUIoxo commented 8 months ago

Description

When I use NotifyCollectionChangedAction.Reset in any kind of ObservableRangeCollection scenario my released App on iPhone crashes after a while without any error message and no catched-Exception in my App. App is just gone/closed.

In the example project below, you can see rising Memory consumption but only when using something like new ObservableRangeCollection(), SelectedStoreItems.RemoveRange(toBeDeleted, System.Collections.Specialized.NotifyCollectionChangedAction.Reset); or SelectedStoreItems.AddRange(newlySelected, System.Collections.Specialized.NotifyCollectionChangedAction.Reset);.

I think I have this effect when any kind of NotifyCollectionChangedAction.Reset or .Clear() is used.

In the past, when saw somthing like this on other places, I detected a rising number of event PropertyNotifyChange subscriptions without unsubscriptions of them. And this number then almost exploded after a while. Here, I use ObservableObject and [ObservableProperty] and not my own implementation of INotifyPropertyChanged, so I can't see it here.

What I can see is, whenever I use new ObservableRangeCollection() or any method that uses NotifyCollectionChangedAction.Reset I see the rising memory consumption. When I use SelectedStoreItems.RemoveRange(toBeDeleted) or SelectedStoreItems.AddRange(newlySelected) without NotifyCollectionChangedAction.Reset, memory consumption stays the same.



Something like this is severe and I detected it at first when my App on my iPhone was just closed reproducibly! Simulator does not close or provide any hints to find the root of all evil. As this concerns ObservableRangeCollection and all other ObservableCollections, I would say this has to be fixed really soon!!



Steps to Reproduce

  1. Start attached example project
  2. Go to file TabView1ViewModel.cs to method private async Task CollectSelectedStoreItems() line 334 and see that method AddRange(...) is used with option SelectedStoreItems.AddRange(newlySelected, System.Collections.Specialized.NotifyCollectionChangedAction.Reset);
  3. In running App click on .NET Bot Icon and select all items from the opened BottomSheet
  4. Close BottomSheet
  5. Open BottomSheet and unselect one of the items
  6. Close BottomSheet
  7. Do the open/close in a loop and select und unselect elements. You can just select and unselect the same item if you like
  8. See that when items are added back in by selecting them can closing the BottomSheet the memory consumption is rising
  9. Go to file TabView1ViewModel.cs again to method private async Task CollectSelectedStoreItems() and use method SelectedStoreItems.AddRange(..); without parameter NotifyCollectionChangedAction.Reset and do the same steps as above
  10. See that Memory consumption stays the same over time
  11. Try using SelectedStoreItems.RemoveRange(toBeDeleted, System.Collections.Specialized.NotifyCollectionChangedAction.Reset); with this parameter
  12. See that also with this on deleting elements the memory consumption rises


Scenario using SelectedStoreItems.AddRange(newlySelected, System.Collections.Specialized.NotifyCollectionChangedAction.Reset):

RisingMemoryConsumption


Scenario using SelectedStoreItems.AddRange(newlySelected):

EvenMemoryConsumption

Link to public reproduction project repository

MemoryLeakExample_21015

Version with bug

8.0.10-nightly.10215 but also other versions including .net 7 and .net 8

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android

Affected platform versions

iOS 17.2

Did you find any workaround?

No response

Relevant log output

No response

symbiogenesis commented 8 months ago

I see this kind of thing, but I think it is related to the CollectionView and not ObservableRangeCollection.

Does a simple Add operation with a regular ObservableCollection leak?

MAUIoxo commented 8 months ago

Absolutely not, when I just use a simple Add(elementToAdd) here. In this case, it is working fine!

This scenario was meant as a workaround to #20927, when it fires the DataTrigger on one of my Entrys or does not consider the data bound value IsValid. Therefore, always one of my Entrys gets the Color that was meant be displayed via DataTrigger when IsValid == false.

Underneath, my data model has Min and Max values and also call OnPropertyChanged(nameof(IsValid)) in this case. On items that are newly selected Min and Max both are 0.

A workaround was the scenario here, in which I wanted to say that the ObservableRangeCollection changed "The contents of the collection changed dramatically."

So, do you think it is the SharpNado CollectionView then or underneath the CollectionView of .NET MAUI anyway?

ghost commented 8 months ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

MAUIoxo commented 8 months ago

@symbiogenesis - Regarding your question if this also is the case when using a simple ObservableCollcetion. The answer is "no" - when just using a simple ObservableCollection and a simple Add(newItem) it does not leak and I could not detect rising memore consumption so far.

But, this also would not solve and be a workaround for #20927, which was the intention using AddRange(..., NotifyCollectionChangedAction.Reset) or ReplaceRange(..., NotifyCollectionChangedAction.Reset) with option NotifyCollectionChangedAction.Reset (or any other option like new ObservableRangeCollection() or .Clear()).

AdamEssenmacher commented 6 months ago

The root cause here appears to be a bug in the 3rd party Sharpnado CollectionView.

The leak expands due to MAUI's propagating leak issue: https://github.com/dotnet/maui/discussions/21918

MAUIoxo commented 6 months ago

Thanks for finding out! By the way, I've read your Blog recently about this subject - really appreciate your expertise and knowledge!

AdamEssenmacher commented 6 months ago

Thanks for finding out! By the way, I've read your Blog recently about this subject - really appreciate your expertise and knowledge!

I've done some work to clear out some other leaks in the Sharpnado CV. Maybe open an issue over there and we can get it looked at.

MAUIoxo commented 6 months ago

I’ve already opened an issue back then. It’s linked under the following item:

Sharpnado CollectionView issue #114

MAUIoxo commented 6 months ago

What do you think about replacing it with a DevExpress CollectionView in terms of Memory Issues and Performance? Have you ever done research on that component?

AdamEssenmacher commented 6 months ago

I haven't tried anything from DevExpress yet.

The TearDownBehavior from my toolkit should be able to compartmentalize this leak to the point where you probably wouldn't notice it.

If you do switch to another collection view, my only advice would be to actively monitor your views and pages for GC during development. As you've seen here, it's just too easy to stumble into small leaks, which then become big ones.