Skip to content

[Regression] COMWrapper Leak when targeting .NET 10 on a WinUI3 project #129191

Description

@macy-kairospower

Description

Updated a Winui3 application from .NET8 -> .NET10 and saw massive increase in memory usage.

  • .NET8 ~250 Mb
  • .NET10 ate up to 13 Gb (Over a week or so) before we stopped the app running.

The app routes a high volume of INotifyPropertyChanged events through the DispatcherQueue which might be part of the cause.
Tested on the latest Winui3 release and the issue was still present.

Posted the issue here because it seems like a runtime issue, not a library issue.

Configuration

Windows 11 26200.8457
Target x64

<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
...
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="2.1.3" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools.WinApp" Version="0.3.1" />

Reproduction Repo Here

https://github.com/macy-kairospower/WinUI__NET10_Leak

Regression

The app sees a buildup of ComWrappers when targeting .net10

.NET8 Memory Profile : Mostly System Types.
Image

.NET10 Memory Profile : an Explosion of Wrappers
Image

Data

The production app eats a ton of memory and it seems to bounce all over the place.
The reproduction app suffers from the same leaks, but sees a plateau of memory usage (unlike the production app)
Image

Analysis

The production application routes a large number of updates through the dispatcher queue using a modified version of the ObservableObject Class
The leak looks like it's coming from the binding, not sure if its a C#/WinRT issue or an Issue with the new GC in .NET10

namespaceObservability.Common{/// <summary>/// Extension of the observable objects class,/// Allows other threads to trigger binding updates/// </summary>publicpartialclassMoreObservableObject:ObservableObject{privatestaticDispatcherQueueQueue{get;set;}publicstaticvoidBindGUIThread(DispatcherQueuequeue){Queue=queue;}publicstaticvoidUpdate(Actionaction){if(Queue==null){action();return;}Queue.TryEnqueue(()=>action());}protectedoverridevoidOnPropertyChanged(PropertyChangedEventArgse){Update(()=>base.OnPropertyChanged(e));}}publicclassMoreObservableCollection<T>:ObservableCollection<T>{publicnewvoidAdd(Titem){MoreObservableObject.Update(()=>base.Add(item));}publicnewvoidClear(){MoreObservableObject.Update(()=>base.Clear());}publicnewvoidRemoveAt(intindex){MoreObservableObject.Update(()=>{if(base.Count>index)base.RemoveAt(index);});}}}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

  • Status
    No status

Relationships

None yet

Development

No branches or pull requests

Issue actions