Context: https://github.com/Vroomer/MAUI-master-detail-memory-leak
Context: https://github.com/symbiogenesis/Maui.DataGrid/tree/memory-leak
Context: dotnet#12039
Using the Visual Studio's `.NET Object Allocation Tracking` profiler
on the sample above I noticed after using the app and taking snapshots:
Snapshot 2:
WeakReference<Microsoft.Maui.Controls.BindableObject>
Count: 1,545
Bytes: 37,080
Snapshot 10:
WeakReference<Microsoft.Maui.Controls.BindableObject>
Count: 1,657
Bytes: 39,768
It appears memory usage is slowly growing by about ~2.45KB each time I
navigate forward a page then return.
f8171b4 introduced a `CleanupTrigger`, but the code has a slight issue:
void CleanUpWeakReferences()
{
if (_targets.Count < _cleanupThreshold)
{
return;
}
_targets.RemoveAll(t => !t.TryGetTarget(out _));
_cleanupThreshold = _targets.Count + CleanupTrigger;
}
Because `_cleanupThreshold` is modified at the bottom of this method,
the `_targets` collection increases by 128 in size over time. The
result being lots of tiny `WeakReference<T>` objects in memory, but
not the actual underlying `T` target.
I did a little research to try to find a better collection than
`List<WeakReference<T>>`:
* https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2
* https://source.dot.net/#Microsoft.Extensions.Caching.Memory/MemoryCache.cs
* https://source.dot.net/#PresentationFramework/System/Windows/Input/KeyboardNavigation.cs,08271c29723aa10a
I did not find one that exactly met our needs, so I wrote a very
simple `WeakList<T>`. It cleans up `WeakReference<T>` that are no
longer alive after N operations. I set this threshold to 32 to start,
we can modify this later if needed. This makes the above samples call
it a couple times for large `CollectionView`'s, which seems reasonable.
After these changes, I'm able to navigate in the app over and over
with stable memory usage:
| ID | Objects | Heap Size |
| -- | --: | --: |
| 1 | 107,885 | 407,645.87 KB |
| 2 | 2,267 | 401,126.22 KB |
This was after navigating through the app *many* times, and it
actually went *down*.
Then I found `Microsoft.Maui.Internals.WeakList<T>`, which I did not
like quite as much as my implementation. I deleted/replaced its usage
adding new unit tests for `WeakList<T>`.
Note that I believe I found one other leak, so this doesn't fully
solve dotnet#12039 yet.