dotnet / dotnet/maui

[leak-scan] VisualElement.Resources roots discarded controls via shared dictionary

Open
#38,288 0 comments 0 reactions 0 assignees View on GitHub
agentic-workflows perf/memory-leak 💦
Dominant language
C#
Stars
23.3k
Forks
2k
Avg merge
1d 15h
Merged PRs (30d)
290

Description

`VisualElement.Resources` strongly subscribes each visual element to the assigned dictionary's internal `ValuesChanged` event. A long-lived/shared `ResourceDictionary` therefore retains discarded controls even when they never enter a visual tree or acquire a handler.

**Empirical result**

A standalone `net10.0` xUnit test referencing shipped `Microsoft.Maui.Controls` **10.0.0** created 30 subjects, each holding a 1 MiB payload:

| Scenario | Payloads alive after 7 full GC cycles |
|---|---:|
| Control (never assigns shared dictionary) | 0/30 |
| Leaky (assigns shared dictionary) | 30/30 |
| Mitigation (replaces it with a fresh dictionary) | 0/30 |

Core test setup:

```csharp
var shared = new ResourceDictionary();

// Repeated 30 times; PayloadProperty contains a new 1 MiB byte[] tracked by WeakReference.
var label = new Label();
label.SetValue(PayloadProperty, payload);
label.Resources = shared;

// Mitigation variant:
label.Resources = new ResourceDictionary();
```

Retention path:

`shared ResourceDictionary -> ValuesChanged delegate -> VisualElement.OnResourcesChanged -> discarded Label -> attached payload`

The current source attaches the strong handler at `src/Controls/src/Core/VisualElement/VisualElement.cs:1170-1190`; replacing the dictionary unsubscribes the old one and releases all mitigation subjects.

Expected behavior: assigning a shared resource dictionary should not permanently root otherwise-unreachable visual elements, or the subscription needs lifecycle teardown when the element is discarded/removed.

This issue was generated by the Daily Memory Leak Hunter workflow using an empirical shipped-package test. No MAUI source build, platform handler, emulator, or native peer was involved.

> Generated by [Daily Memory Leak Hunter](https://github.com/dotnet/maui/actions/runs/34124516166) · gpt56 · 213.6 AIC · ⌖ 12.3 AIC · ⊞ 32.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+is%3Aissue+%22gh-aw-workflow-call-id%3A+dotnet%2Fmaui%2Fdaily-leak-hunter%22&type=issues)

Contributor guide

Open the contributing guide

Research direction

Start in src/Controls/src/Core/VisualElement/VisualElement.cs:1170-1190 and run the standalone net10.0 xUnit reproduction described in the issue. Trace the shared ResourceDictionary subscription and its teardown behavior. Done means discarded controls assigned a shared dictionary are no longer retained, with the GC-based reproduction confirming payloads are collectible.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.