What it is
LeakAnalyser answers one question: after this view looks finished, is it still alive? It snapshots the visual tree, waits for lifecycle inference, then runs WeakReference plus forced GC. If the object survives, OnLeaked fires. If it is collected, OnCollected fires.
Unloaded → “done with this view?” → Monitor (Debug) and/or TearDownStrategies
| Strategy | Effect |
|---|---|
| DetectOnly | No teardown. The graph stays intact so you can see a real leak. |
| DisconnectHandlers (default) | Walk visual children and call DisconnectHandler() per view. Does not null BindingContext, Content, or Parent. |
| Compartmentalize | Clear common managed references, invoke TearDown.OnTearDown, then disconnect that element's handler. |
DisconnectHandlers is a safe tree walk: it honors HandlerDisconnectPolicy.Manual, TearDown.Suppress, nested Cascade islands, and try/catches each disconnect.
What compartmentalize clears
Each clear is isolated. A throwing setter is logged; teardown continues.
- BindingContext, Parent, and logical children
- VisualElement.Behaviors and Resources
- View.GestureRecognizers
- Label.FormattedText and span gestures
- ItemsView / legacy ListView ItemsSource and ItemTemplate
- Content on ContentView, Border, ContentPage, and ScrollView
CommunityToolkit hooks stay intact because they are attached properties, not items in Behaviors. OnTearDown runs only for Compartmentalize, and only when the element still has a handler — use it to stop animations before disconnect.
Lifecycle (“done with”)
Automatic behaviors do not tear down on every Unloaded. They wait until the view looks finished:
- Host Page was popped from an active NavigationPage
- View unloaded and is not hosted in a Page (template swap / detached)
- Hosted in a NavigationPage that is itself unloaded and not suppressed
- Otherwise wait 100 ms; skip if still in NavigationStack / ModalStack, still hosted by Shell, or under a Tab
Skipped: the NavigationPage container itself, views under a nav that still has modals, suppressed views, and cached pages (you must Suppress). This is best-effort inference, not a Shell / popup / Hybrid navigation framework.
XAML and C#
Put TearDown after LeakMonitor so teardown does not run before the monitor snapshots the tree.
<ContentPage xmlns:la="clr-namespace:Plugin.Maui.LeakAnalyser;assembly=Plugin.Maui.LeakAnalyser"
la:LeakMonitor.Cascade="True"
la:LeakMonitor.Name="OrdersPage"
la:TearDown.Cascade="True"
la:TearDown.Strategy="DisconnectHandlers">The same attached properties are available in C#:
LeakMonitor.SetCascade(page, true);
TearDown.SetCascade(page, true);
TearDown.SetStrategy(page, TearDownStrategy.DisconnectHandlers);
page.Monitor();
page.TearDown(TearDownStrategy.DisconnectHandlers);
var nav = LeakGraph.GetFirstSelfOrParentOfType<NavigationPage>(this);
LeakMonitor.SetSuppress(nav, true);
TearDown.SetSuppress(nav, true);- Temporary unloads (Browser.OpenAsync) should suppress the host NavigationPage, then clear Suppress on Loaded.
- ControlTemplates: put Cascade on each template root, not only the host control.
- Cached pages and Shell tabs stay alive on purpose — Suppress them or they will look like leaks.
Options
| Option | Default | Role |
|---|---|---|
| DefaultTearDownStrategy | DisconnectHandlers | Used when TearDown.Strategy is unset. |
| OnLeaked | null | Fired when a watched target is still alive after forced collections. |
| OnCollected | null | Fired when a watched target is collected. |
| MaxCollections | 10 | Forced GC passes before a still-alive target is reported as leaked. |
| MillisecondsBetweenCollections | 200 | Delay between collection passes. |
| CustomMonitor | null | Replace the default IGarbageCollectionMonitor. |
CollectionTarget carries Name, ObjectType, Screen (host page at snapshot time), and a short WeakReference. Use Name in alerts and breadcrumbs.
When to use it
- How do I detect MAUI page / view leaks after navigation?
- Why is BindingContext still alive after pop?
- How do I disconnect handlers when a view is finished?
Do not use this package if you need retain-path diagnosis (use a profiler), only crash / ANR breadcrumbs (Diagnostics), or only startup / page / API timings (Performance).
Platforms and version
Version 1.0.1. Target frameworks: net10.0, net10.0-android (API 21+), net10.0-ios (iOS 15+), net10.0-maccatalyst (15+), net10.0-windows (10.0.17763+; the Windows TFM is included when the package is packed on Windows).
Install without --prerelease. Plugin.Maui.LeakAnalyser on NuGet