-
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
Memory leaks on latest .NET 8 preview with Windows #16436
Comments
@jonathanpeppers You may have some thoughts. Manually running GC doesn't help. |
Everywhere you see https://github.com/symbiogenesis/Maui.DataGrid/blob/net8-memory-leak/Maui.DataGrid/DataGrid.xaml.cs So far looking at this, I see several potential leaks in this Have you seen this wiki page? It might help you diagnose and track these down: |
My code is already using the WeakEventManager for all of my events, and I unsubscribe to all of the events at what I think is the proper times, with the exception of a minor change I just made. Not sure what else I ought to be doing. That link shows some similar event usage, and links to a PR that implements the WeakEventManager approach that I'm already using. |
I think I see now. So the subscriptions to events that are located outside of MAUI in the .NET Framework are unsafe, and need a proxy wrapper to make them safe. I will implement that and try again. |
I just implemented that in the linked branch, except where WeakEventManager was being used, and the memory leaks remain |
Especially on Windows, you should be able to see a tree of objects from a memory snapshot. What object is holding on to another object? If you can share their fully qualified name or the In addition to the https://github.com/dotnet/maui/wiki/Memory-Leaks#narrowing-down-the-leak Does the problem go away if you swap your custom |
Can you comment on:
Your latest screenshot shows the |
The DataGrid control is mainly a wrapper for the CollectionView. Each row is a Grid, with identical ColumnDefinitions. I personally think that the CollectionView is leaking. |
Changing the CollectionView to a ListView does not resolve the problem. |
Can you share a branch of your sample that uses https://github.com/symbiogenesis/Maui.DataGrid/tree/net8-memory-leak It isn't using the |
I just updated this branch to just use a simple CollectionView, and the ListView version is commented out below it. With this simple version, it also leaks, yes. |
Specifically, I think it is only when using an ItemTemplate, and this issue has existed since Xamarin.Forms |
I found this issue with But let me see if something similar is happening with |
This might have more to do with the Grid being inside of the ItemTemplate, rather than the ItemTemplate in general. |
@symbiogenesis I did some debugging, and it appears the way the
maui/src/Controls/src/Core/Platform/Windows/CollectionView/ItemTemplateContextList.cs Lines 25 to 29 in 040f2f1
And so Now, I do see some things in the code here that could be improved -- but I'm not seeing a leak so far. Do you feel like you are seeing something different than me? I will see if ListView feels any different. |
One reason that I think it leaks is that navigating away from the page doesn't free up any memory. Even with a manual garbage collect. I also tend to use more complicated DataTemplates, and with these scrolling back to rows that were already loaded does increase the memory usage. |
Context: dotnet#16436 Context: https://github.com/symbiogenesis/Maui.DataGrid/tree/net8-memory-leak A customer sample has a `CollectionView` with 150,000 data-bound rows. Debugging what happens at runtime, I found we effectively were doing: _itemTemplateContexts = new List<ItemTemplateContext>(capacity: 150_000); for (int n = 0; n < 150_000; n++) { _itemTemplateContexts.Add(null); } Then the items were created as you scroll each row into view: if (_itemTemplateContexts[index] == null) { _itemTemplateContexts[index] = context = new ItemTemplateContext(_itemTemplate, _itemsSource[index], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext); } return _itemTemplateContexts[index]; This code accesses the indexer multiple times, into a `List<T>` with 150,000 `null` items. To improve this: * use a `Dictionary<int, T>` instead, just let it size dynamically. * use `TryGetValue(..., out var context)`, so each call accesses the indexer one less time than before. * use either the bound collection's size or 64 (whichever is smaller) as a rough estimate of how many might fit on screen at a time. Taking a memory snapshot of the app after startup: Before: Heap Size: 82,899.54 KB After: Heap Size: 81,768.76 KB Which is saving about 1MB of memory on launch. In this case, it feels better to just let the `Dictionary` size itself with an estimate of what `capacity` will be.
Can you update the sample to show this behavior? Or some examples of I tried I opened this one, as an obvious improvement looking at the existing code: |
Context: dotnet#16436 Context: https://github.com/symbiogenesis/Maui.DataGrid/tree/net8-memory-leak A customer sample has a `CollectionView` with 150,000 data-bound rows. Debugging what happens at runtime, I found we effectively were doing: _itemTemplateContexts = new List<ItemTemplateContext>(capacity: 150_000); for (int n = 0; n < 150_000; n++) { _itemTemplateContexts.Add(null); } Then the items were created as you scroll each row into view: if (_itemTemplateContexts[index] == null) { _itemTemplateContexts[index] = context = new ItemTemplateContext(_itemTemplate, _itemsSource[index], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext); } return _itemTemplateContexts[index]; This code accesses the indexer multiple times, into a `List<T>` with 150,000 `null` items. To improve this: * use a `Dictionary<int, T>` instead, just let it size dynamically. * use `TryGetValue(..., out var context)`, so each call accesses the indexer one less time than before. * use either the bound collection's size or 64 (whichever is smaller) as a rough estimate of how many might fit on screen at a time. Taking a memory snapshot of the app after startup: Before: Heap Size: 82,899.54 KB After: Heap Size: 81,768.76 KB Which is saving about 1MB of memory on launch. In this case, it feels better to just let the `Dictionary` size itself with an estimate of what `capacity` will be.
Context: #16436 Context: https://github.com/symbiogenesis/Maui.DataGrid/tree/net8-memory-leak A customer sample has a `CollectionView` with 150,000 data-bound rows. Debugging what happens at runtime, I found we effectively were doing: _itemTemplateContexts = new List<ItemTemplateContext>(capacity: 150_000); for (int n = 0; n < 150_000; n++) { _itemTemplateContexts.Add(null); } Then the items were created as you scroll each row into view: if (_itemTemplateContexts[index] == null) { _itemTemplateContexts[index] = context = new ItemTemplateContext(_itemTemplate, _itemsSource[index], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext); } return _itemTemplateContexts[index]; This code accesses the indexer multiple times, into a `List<T>` with 150,000 `null` items. To improve this: * use a `Dictionary<int, T>` instead, just let it size dynamically. * use `TryGetValue(..., out var context)`, so each call accesses the indexer one less time than before. * use either the bound collection's size or 64 (whichever is smaller) as a rough estimate of how many might fit on screen at a time. Taking a memory snapshot of the app after startup: Before: Heap Size: 82,899.54 KB After: Heap Size: 81,768.76 KB Which is saving about 1MB of memory on launch. In this case, it feels better to just let the `Dictionary` size itself with an estimate of what `capacity` will be.
I created a simple project, specified this XAML and using mouse scrolling up and down, you can increase memory consumption to over 3 gigabytes
(ItemsSource is simple List with 1000 elements) |
NET8, Controls nuget lib version 8.0.6 |
@scriptBoris I see the same thing as you. I just submitted PR #20036 that may help a bit with CollectionView on Windows. I'm sure there's lots more to do. |
Verified this issue with Visual Studio Enterprise 17.10.0 Preview 1. Can repro on Windows platform with sample project. I disabled XAML Hot Reload, and after successful debugging, I scrolled up and down, but the problem still occurred. |
Here is a sample where you can reproduce the issue. Run the application and click on the "Reload Data" button. On every data load, you will see that memory consumption is increasing. The sample contains a simple data template with a grid. |
Description
A data grid that I am working on leaks memory massively as you scroll around when there are a lot of records.
Steps to Reproduce
Just run the sample I provided, and start scrolling, etc.
Link to public reproduction project repository
https://github.com/symbiogenesis/Maui.DataGrid/tree/net8-memory-leak
Version with bug
8.0.0-preview.6.8686
Last version that worked well
Unknown/Other
Affected platforms
Windows
Affected platform versions
All
Did you find any workaround?
No.
Relevant log output
No response
The text was updated successfully, but these errors were encountered: