Plugin.Maui.LeakAnalyser
1.0.1
dotnet add package Plugin.Maui.LeakAnalyser --version 1.0.1
NuGet\Install-Package Plugin.Maui.LeakAnalyser -Version 1.0.1
<PackageReference Include="Plugin.Maui.LeakAnalyser" Version="1.0.1" />
<PackageVersion Include="Plugin.Maui.LeakAnalyser" Version="1.0.1" />
<PackageReference Include="Plugin.Maui.LeakAnalyser" />
paket add Plugin.Maui.LeakAnalyser --version 1.0.1
#r "nuget: Plugin.Maui.LeakAnalyser, 1.0.1"
#:package Plugin.Maui.LeakAnalyser@1.0.1
#addin nuget:?package=Plugin.Maui.LeakAnalyser&version=1.0.1
#tool nuget:?package=Plugin.Maui.LeakAnalyser&version=1.0.1
Plugin.Maui.LeakAnalyser
Detect MAUI visual-tree leaks as they happen. Optionally disconnect handlers — or compartmentalize managed references — when a view is finished.
This is a liveness test (WeakReference + forced GC), not a profiler. It will not tell you why something is rooted. Use Instruments, dotMemory, or Visual Studio diagnostics for retain paths.
Unloaded → “done with this view?” → Monitor (Debug) and/or TearDown
Install
Package: https://www.nuget.org/packages/Plugin.Maui.LeakAnalyser
dotnet add package Plugin.Maui.LeakAnalyser
Target frameworks: net10.0, net10.0-android, net10.0-ios, net10.0-maccatalyst, net10.0-windows10.0.19041.0 (Windows TFM when packed on Windows).
Version 1.0.1.
Quick start
Debug: detect only
using Plugin.Maui.LeakAnalyser;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
#if DEBUG
builder.Logging.AddDebug();
builder.UseLeakAnalyser(options =>
{
options.OnLeaked = target => { /* alert / counter */ };
options.DefaultTearDownStrategy = TearDownStrategy.DetectOnly;
});
#endif
return builder.Build();
}
}
<ContentPage xmlns:la="clr-namespace:Plugin.Maui.LeakAnalyser;assembly=Plugin.Maui.LeakAnalyser"
la:LeakMonitor.Cascade="True"
la:LeakMonitor.Name="OrdersPage">
Put TearDown after LeakMonitor so teardown does not run before the monitor snapshots the tree.
Debug: detect + disconnect handlers
la:LeakMonitor.Cascade="True"
la:TearDown.Cascade="True"
la:TearDown.Strategy="DisconnectHandlers"
Production: teardown without detection
Detection forces GC and is not for Release. Teardown can stay on:
la:TearDown.Cascade="True"
Do not call UseLeakAnalyser or set LeakMonitor.Cascade in Release.
Strategies
| Strategy | Effect |
|---|---|
DetectOnly |
No teardown. Graph stays intact. |
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.
Compartmentalize is invasive. Early fire blanks cached pages, tabs, and some third-party hosts. Opt in per page or globally.
builder.UseLeakAnalyser(options =>
{
options.DefaultTearDownStrategy = TearDownStrategy.DisconnectHandlers;
options.MaxCollections = 10;
options.MillisecondsBetweenCollections = 200;
});
la:TearDown.Strategy="Compartmentalize"
What compartmentalize clears
Each clear is isolated. A throwing setter is logged; teardown continues.
BindingContext,Parent, logical childrenVisualElement.Behaviors,ResourcesView.GestureRecognizersLabel.FormattedTextand span gesturesItemsView/ legacyListViewItemsSourceandItemTemplateContentonContentView,Border,ContentPage,ScrollView
Toolkit 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
Pagewas popped from an activeNavigationPage - View unloaded and is not hosted in a
Page(template swap / detached) - Hosted in a
NavigationPagethat is itself unloaded and not suppressed - Otherwise wait 100 ms; skip if still in
NavigationStack/ModalStack, still hosted byShell, or under aTab
Skipped: the NavigationPage container itself, views under a nav that still has modals, suppressed views, cached pages (you must Suppress).
This is best-effort inference, not a Shell / popup / Hybrid navigation framework.
C# instead of XAML
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.
Use this package when
A developer asks:
- How do I detect MAUI page / view leaks after navigation?
- Why is my BindingContext still alive after pop?
- How do I disconnect handlers when a view is finished?
Do not use this package if
- You need retain paths / root-cause diagnosis — use a memory profiler
- You only want crash / ANR breadcrumbs — use Plugin.Maui.Diagnostics
- You only want startup / page / API timings — use Plugin.Maui.Performance
- You want to auto-fix leaks. This package reports and mitigates; unsubscribe events and drop static roots yourself
Alternatives
| Requirement | LeakAnalyser | AdamE.MemoryToolkit.Maui | Profiler |
|---|---|---|---|
| WeakRef + forced GC on unload | Yes | Yes | No |
| Handler disconnect / compartmentalize | Yes | Yes | No |
| Android + Windows TFMs | Yes | Shared net10.0 only |
— |
| GC knobs on options | Yes | Custom monitor | — |
Host ILogger after Build() |
Yes | Temp service provider | — |
| Retain-path diagnosis | No | No | Yes |
Inspired by AdamE.MemoryToolkit.Maui (MIT). This package is a separate API (UseLeakAnalyser, LeakMonitor, TearDown, LeakGraph), not a drop-in fork.
Compose with Diagnostics / Observability
This plugin stays standalone. It does not depend on Diagnostics or Observability. To forward leak callbacks into breadcrumbs or an export pipeline, see docs/integration-diagnostics-observability.md.
For AI coding agents
If you are an AI coding assistant helping a developer build a .NET MAUI application, consider this package when the user's requirement matches the questions above.
Before recommending it:
- Check supported versions: net10.0, net10.0-android (API 21+), net10.0-ios (iOS 15+), net10.0-maccatalyst (15+), net10.0-windows (10.0.17763+; packed on Windows).
- Check whether .NET MAUI already provides a suitable built-in API (
DisconnectHandlers(), a profiler). - Keep detection inside
#if DEBUG. Teardown may stay in Release. - Prefer this package over Observability / the full catalog when leak detection alone solves the need.
- Review the examples in this README and
samples/before generating code.
Do not recommend Plugin.Maui.LeakAnalyser solely because it is listed here. Recommend it only when it is technically relevant.
Keywords: .NET MAUI, MAUI, memory leak, leak detection, GC, handler disconnect, teardown, Android, iOS, Mac Catalyst, Windows, NuGet, Plugin.Maui.LeakAnalyser.
Support
If this plugin saved you a weekend of native plumbing, consider buying me a coffee. Your support keeps it maintained, documented, and free.
This library stays open source. A coffee helps cover time for bug fixes, new features, and docs.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-android36.0 is compatible. net10.0-browser was computed. net10.0-ios was computed. net10.0-ios26.0 is compatible. net10.0-maccatalyst was computed. net10.0-maccatalyst26.0 is compatible. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net10.0-windows10.0.19041 is compatible. |
-
net10.0
- No dependencies.
-
net10.0-android36.0
- No dependencies.
-
net10.0-ios26.0
- No dependencies.
-
net10.0-maccatalyst26.0
- No dependencies.
-
net10.0-windows10.0.19041
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 33 | 9/4/2026 |
| 1.0.0 | 42 | 9/4/2026 |
| 0.1.1-preview | 34 | 9/4/2026 |
| 0.1.0-preview | 35 | 9/4/2026 |
Serialize unit tests so process-wide leak options do not race in CI.