Skip to content

iOS/Mac Catalyst MapHandler leaks MAUI Map views and MapElements through MapPool #35479

Description

@AdamEssenmacher

Description

On iOS and Mac Catalyst, MAUI Map pages that contain MapElements can remain retained after they are popped from navigation. The repro pushes and pops 30 real Shell pages containing Map controls with Circle elements, then forces full GC and checks weak references.

The control scenario, using maps without MapElements, collects normally. The MapElements scenario retains all 30 MAUI Map views, all 30 payload view models, and all 1,200 map elements, retaining about 60 MB of managed payload after GC.

Clearing Map.MapElements in OnDisappearing() avoids the retention, which suggests stale map-element tracking/native overlay state is held through the iOS/Mac Catalyst pooled native map path rather than normal app ownership. Developers should not need to manually clear Map.MapElements for popped pages to become collectible.

Steps to Reproduce

  1. Check out the repro branch:
git clone https://github.com/AdamEssenmacher/maui.git
cd maui
git checkout repro/map-pool-map-elements-leak
  1. Build the in-tree MAUI build tasks:
dotnet build Microsoft.Maui.BuildTasks.slnf
  1. Run the Mac Catalyst repro app:
dotnet run --project src/Controls/samples/MapPoolLeakRepro/MapPoolLeakRepro.csproj -f net10.0-maccatalyst -p:RuntimeIdentifier=maccatalyst-arm64
  1. Leave the default settings:
Pages/run: 30
MapElements/page: 40
Payload MB/page: 2
Dwell ms/page: 120
  1. Click Run control and wait for completion.

  2. Click Run leaky MapElements and wait for completion.

  3. Optional: click Run mitigation to show that clearing Map.MapElements before disconnect avoids the retention.

Actual Result

The control scenario collects normally:

MAUI Map views: 0/30
payload view models: 0/30
map elements: 0/0
Payload retained by alive view models: 0 B

The MapElements scenario retains the old page graph after full GC:

MAUI Map views: 30/30
payload view models: 30/30
map elements: 1200/1200
Payload retained by alive view models: 60.0 MB
Managed heap delta after GC: 63.7 MB

Expected Result

After the pages are popped and full GC runs, the old MAUI Map views, their payload view models, and MapElements should be collectible, matching the control scenario.

Notes

The mitigation scenario clears Map.MapElements in OnDisappearing() and returns to 0/30 retained maps/view models and 0/1200 retained elements. This demonstrates an app-level workaround and points to stale MapElement state retained through the iOS/Mac Catalyst pooled native map path.

Link to public reproduction project repository

https://github.com/AdamEssenmacher/maui/tree/repro/map-pool-map-elements-leak/src/Controls/samples/MapPoolLeakRepro

Version with bug

10.0.60

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, macOS

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-controls-mapMap / Mapsi/regressionThis issue described a confirmed regression on a currently supported versionperf/memory-leak 💦Memory usage grows / objects live forever (sub: perf)platform/iosplatform/macosmacOS / Mac Catalystregressed-in-10.0.60s/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions