Skip to content

TabbedPage leaks renderer/manager when BarBackground uses shared GradientBrush resource #35469

Description

@AdamEssenmacher

Description

A non-Shell TabbedPage can leak its platform renderer/manager when BarBackground is set from a shared GradientBrush, such as an app-level XAML resource used through a Style.

The platform code subscribes to GradientBrush.InvalidateGradientBrushRequested when applying TabbedPage.BarBackground, but the subscription is not removed when the TabbedPage handler/renderer disconnects. If the brush is app-lifetime, the brush keeps the old platform subscriber alive.

Observed results:

  • Android: shared brush retains Microsoft.Maui.Controls.Handlers.TabbedPageManager, and the removed TabbedPage plus child page remain alive.
  • iOS: shared brush retains Microsoft.Maui.Controls.Handlers.Compatibility.TabbedRenderer.
  • Control path: clearing BarBackground before removal leaves zero brush subscribers and allows the captured subscriber to collect.

Steps to Reproduce

  1. Clone the repro branch:
git clone https://github.com/AdamEssenmacher/maui.git
cd maui
git checkout tabbed-page-leak-repro
  1. Build and run the repro project.

Android:

bash ./eng/common/dotnet.sh build src/Controls/samples/TabbedPageBarBackgroundLeakRepro/Maui.Controls.TabbedPageBarBackgroundLeakRepro.csproj -f net10.0-android -p:IncludeAndroidTargetFrameworks=true
adb install -r artifacts/bin/Maui.Controls.TabbedPageBarBackgroundLeakRepro/Debug/net10.0-android/com.microsoft.maui.repros.tabbedpagebarbackgroundleak-Signed.apk
adb shell monkey -p com.microsoft.maui.repros.tabbedpagebarbackgroundleak 1

iOS simulator:

bash ./eng/common/dotnet.sh build src/Controls/samples/TabbedPageBarBackgroundLeakRepro/Maui.Controls.TabbedPageBarBackgroundLeakRepro.csproj -f net10.0-ios -p:IncludeIosTargetFrameworks=true -p:RuntimeIdentifier=iossimulator-arm64
xcrun simctl install booted artifacts/bin/Maui.Controls.TabbedPageBarBackgroundLeakRepro/Debug/net10.0-ios/iossimulator-arm64/Maui.Controls.TabbedPageBarBackgroundLeakRepro.app
xcrun simctl launch booted com.microsoft.maui.repros.tabbedpagebarbackgroundleak
  1. Wait for the repro to auto-run. It creates a TabbedPage whose app-level Style sets BarBackground from a shared LinearGradientBrush resource, replaces Window.Page, forces GC, then compares against a control path that clears BarBackground before removal.

  2. Observe the result text in the app.

Expected affected result:

Leak reproduced: app resource style gradient retained the removed TabbedPage renderer/manager.

Android leak path shows:

Brush subscribers: 1
Subscriber targets: Microsoft.Maui.Controls.Handlers.TabbedPageManager
Captured subscriber target: alive
Previous child page: alive
TabbedPage: alive

iOS leak path shows:

Brush subscribers: 1
Subscriber targets: Microsoft.Maui.Controls.Handlers.Compatibility.TabbedRenderer
Captured subscriber target: alive

Control path should show:

Brush subscribers: 0
Captured subscriber target: collected

Link to public reproduction project repository

https://github.com/AdamEssenmacher/maui/tree/tabbed-page-leak-repro/src/Controls/samples/TabbedPageBarBackgroundLeakRepro

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, Android, I was not able test on other platforms

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions