Skip to content
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 Style and AttachedCollection #13260

Merged
merged 1 commit into from
Feb 14, 2023

Commits on Feb 13, 2023

  1. [controls] fix memory leak in Style and AttachedCollection

    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.
    jonathanpeppers committed Feb 13, 2023
    Configuration menu
    Copy the full SHA
    9e93a77 View commit details
    Browse the repository at this point in the history