Freigeben über


Leistungsprofilerstellung

Leistungsprofilierung ist der Prozess der Messung der Leistung einer Anwendung, um Bereiche zur Verbesserung zu identifizieren. .NET MAUI und Clientanwendungen interessieren sich im Allgemeinen für:

  • Startzeit: Die Zeit, die die Anwendung benötigt, um den ersten Bildschirm zu starten und anzuzeigen.
  • CPU-Auslastung: Wenn bestimmte Methoden zu viel CPU-Zeit in Anspruch nehmen: entweder über viele Aufrufe oder lange Ausgeführte Vorgänge.
  • Speicherauslastung: Wenn viele Zuordnungen außerhalb des Grunds erfolgen oder Speicherverluste auftreten.

Die Techniken und Tools zur Verbesserung dieser Metriken sind unterschiedlich, was wir in diesem Leitfaden aufklären möchten. Die Tools, die zum Profilieren von .NET MAUI-Anwendungen verwendet werden, können auch je nach Plattform variieren. In diesem Leitfaden werden Android-, iOS-, Mac Catalyst- und Windows-Profilerstellungsansätze behandelt.

Von Bedeutung

Profilieren Sie stets Ihre Builds Release, um genaue Leistungsmessungen zu erhalten. Debug-Builds verwenden den Interpreter (UseInterpreter=true), um die C#-Hot-Reload-Unterstützung bereitzustellen, was die Leistung erheblich beeinträchtigt und zu unrealistischen Ergebnissen führt.

Voraussetzungen

Installieren von Diagnosetools

Um .NET MAUI-Anwendungen unter iOS und Android zu profilieren, müssen Sie die folgenden globalen .NET-Tools installieren:

  • dotnet-trace – Erfasst CPU-Ablaufverfolgungen und Leistungsdaten
  • dotnet-dsrouter – Leitet Diagnoseverbindungen von Remotegeräten an Ihren lokalen Computer weiter.
  • dotnet-gcdump – Sammelt Speicherabbilder zur Analyse der verwalteten Speicherauslastung.

Sie können diese Tools mit den folgenden Befehlen installieren:

$ dotnet tool install -g dotnet-trace
You can invoke the tool using the following command: dotnet-trace
Tool 'dotnet-trace' was successfully installed.
$ dotnet tool install -g dotnet-dsrouter
You can invoke the tool using the following command: dotnet-dsrouter
Tool 'dotnet-dsrouter' was successfully installed.
$ dotnet tool install -g dotnet-gcdump
You can invoke the tool using the following command: dotnet-gcdump
Tool 'dotnet-gcdump' was successfully installed.

Hinweis

Sie benötigen mindestens Version 9.0.652701 aller Diagnosetools, um die in diesem Handbuch beschriebenen Features zu verwenden. Überprüfen Sie dotnet-trace, dotnet-dsrouter und dotnet-gcdump auf NuGet für die neuesten Versionen.

Ab Version 9.0.652701 enthalten sowohl dotnet-trace als auch dotnet-gcdump eine --dsrouter Option, die dotnet-dsrouter automatisch als Unterprozess startet und verwaltet. Dadurch entfällt die Notwendigkeit, dotnet-dsrouter separat auszuführen, was den Profilerstellungsworkflow erheblich vereinfacht.

Eine Live-Demo zur Verwendung dieser Tools finden Sie in der .NET Conf-Sitzung ,.NET Diagnostic Tooling with AI".

Wie die Tools zusammenarbeiten

Um diese Diagnosetools unter iOS und Android zu verwenden, arbeiten mehrere Komponenten zusammen:

  • Die globalen .NET-Tools (dotnet-trace, dotnet-gcdump, dotnet-dsrouter) werden auf Ihrem Entwicklungscomputer ausgeführt.
  • Die Mono-Diagnosekomponente (libmono-component-diagnostics_tracing.so) ist in Ihrem Anwendungspaket enthalten.
  • dotnet-dsrouter leitet die Diagnoseverbindung vom Remotegerät oder Emulator an einen lokalen Port auf Ihrem Computer weiter.
  • Die Diagnosetools stellen eine Verbindung mit diesem lokalen Port her, um Profilerstellungsdaten zu sammeln.

Die --dsrouter Option in dotnet-trace und in dotnet-gcdump behandelt automatisch die Komplexität des Startens von dotnet-dsrouter und Koordinierens der Verbindung.

Erstellen Ihrer Anwendung für Profilerstellung

Zum Aktivieren der Profilerstellung muss Ihre Anwendung mit speziellen MSBuild-Eigenschaften erstellt werden, die die Diagnosekomponenten enthalten und die Verbindung mit den Profilerstellungstools konfigurieren.

Grundlegendes zu Diagnoseeigenschaften

Die folgenden MSBuild-Eigenschaften steuern, wie Ihre Anwendung mit den Diagnosetools kommuniziert:

  • DiagnosticAddress: Die IP-Adresse, an der dotnet-dsrouter lauscht. Verwenden Sie 10.0.2.2 für Android-Emulatoren (dies ist die Loopback-Adresse des Hostcomputers aus der Perspektive des Emulators) und 127.0.0.1 für physische Geräte und iOS.

  • DiagnosticPort: Die Portnummer für die Diagnoseverbindung (Standardeinstellung ist 9000).

  • DiagnosticSuspend: Wenn truedie Anwendung wartet, bis der Profiler eine Verbindung herstellt, bevor sie gestartet wird. Sobald false die Anwendung startet, kann der Profiler später eine Verbindung herstellen. Verwenden Sie true für die Startprofilerstellung und false für die Laufzeitprofilerstellung und Speicherabbilder.

  • DiagnosticListenMode: Wird auf connect für Android festgelegt (die App stellt eine Verbindung zu dotnet-dsrouter her), oder auf listen für iOS (die App wartet darauf, dass dotnet-dsrouter eine Verbindung herstellt).

  • EnableDiagnostics: Wenn true vorhanden ist, wird die Mono-Diagnosekomponente in das Anwendungspaket aufgenommen. Dies wird implizit festgelegt, wenn Sie eine der Diagnostic* MSBuild-Eigenschaften festlegen. Diese Eigenschaft funktioniert unter Android, iOS und Mac Catalyst.

Hinweis

Bei Verwendung von CoreCLR (derzeit experimentell unter Android mit geplanter iOS-Unterstützung) ist die Diagnosekomponente in die Laufzeit integriert und EnableDiagnostics nicht erforderlich.

Befehlsbeispiele für Build

Wenn Sie dotnet-trace oder dotnet-gcdump mit der --dsrouter-Option ausführen, zeigt das Tool Anweisungen zum Erstellen Ihrer Anwendung an. Beispiel:

Für Android-Emulatoren:

dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=10.0.2.2 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=false -p:DiagnosticListenMode=connect

Für Android-Geräte:

dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=false -p:DiagnosticListenMode=connect

Für iOS-Geräte und Simulatoren:

dotnet build -t:Run -c Release -f net10.0-ios -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=false -p:DiagnosticListenMode=listen

Hinweis

Verwenden Sie -f net10.0-android oder -f net10.0-ios für Projekte mit mehreren Zielframeworks in $(TargetFrameworks).

Von Bedeutung

Anwendungen, die mit diesen Diagnoseeigenschaften erstellt wurden, sollten nur für Entwicklung und Tests verwendet werden. Veröffentlichen Sie niemals Builds mit Diagnosekomponenten, die für die Produktion aktiviert sind, da sie Endpunkte mit tieferen Einblicken in den Code Ihrer Anwendung verfügbar machen können.

Profilierung der CPU-Auslastung

Das dotnet-trace Tool sammelt CPU-Samplinginformationen in Formaten wie .nettrace und .speedscope.json. Diese Ablaufverfolgungen zeigen Ihnen die für jede Methode aufgewendete Zeit, um Leistungsengpässe in Ihrer Anwendung zu identifizieren.

Der Workflow für die CPU-Profilerstellung hängt davon ab, ob Sie startzeit- oder Profilerstellungs-Laufzeitvorgänge messen. Der Hauptunterschied ist die -p:DiagnosticSuspend MSBuild-Eigenschaft.

Profilerstellungsstartzeit

Um genaue Startzeitmessungen zu erfassen, pausieren Sie den Start der Anwendung, bis der Profiler bereit ist. Dadurch wird sichergestellt, dass Sie die gesamte Startsequenz von Anfang an erfassen.

  1. Öffnen Sie ein Terminal und beginnen Sie dotnet-trace mit der Option --dsrouter.

    dotnet-trace collect --dsrouter android-emu --format speedscope
    

    Oder für ein physisches Android-Gerät:

    dotnet-trace collect --dsrouter android --format speedscope
    

    Verwenden Sie --dsrouter ios für iOS-Geräte und --dsrouter ios-sim für Simulatoren.

  2. Erstellen Sie Ihre Anwendung in einem anderen Terminal und bereiten Sie sie darauf vor, beim Start mit -p:DiagnosticSuspend=true anzuhalten.

    Für Android-Emulatoren:

    dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=10.0.2.2 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=true -p:DiagnosticListenMode=connect
    

    Für Android-Geräte:

    dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=true -p:DiagnosticListenMode=connect
    

    Für iOS (Geräte und Simulatoren):

    dotnet build -t:Run -c Release -f net10.0-ios -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=true -p:DiagnosticListenMode=listen
    
  3. Ihre Anwendung wird auf dem Begrüßungsbildschirm angehalten und wartet darauf, dass dotnet-trace eine Verbindung herstellt. Nach der Verbindung wird die Anwendung gestartet und dotnet-trace beginnt mit der Aufzeichnung.

  4. Ermöglichen Sie es Ihrer Anwendung, den Anfangsbildschirm vollständig zu starten und zu erreichen.

  5. Drücken Sie <Enter> im dotnet-trace-Terminal, um die Aufzeichnung zu beenden.

Die Ablaufverfolgungsdatei wird im aktuellen Verzeichnis gespeichert. Verwenden Sie die -o Option, um ein anderes Ausgabeverzeichnis anzugeben.

Profiling-Laufzeitoperationen

Um bestimmte Vorgänge während der Laufzeit zu profilieren (z. B. Schaltflächentippen, Navigation oder Scrollen), verwenden -p:DiagnosticSuspend=false und verbinden Sie den Profiler, nachdem die Anwendung gestartet wurde.

  1. Erstellen und bereitstellen Ihrer Anwendung mit -p:DiagnosticSuspend=false:

    dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=false -p:DiagnosticListenMode=connect
    
  2. Navigieren Sie zum Bereich Ihrer Anwendung, den Sie profilieren möchten.

  3. Beginnen Sie dotnet-trace mit der --dsrouter Option:

    dotnet-trace collect --dsrouter android --format speedscope
    
  4. Führen Sie den Vorgang aus, den Sie profilieren möchten.

  5. Drücken Sie <Enter> , um die Ablaufverfolgung zu beenden.

Dieser Ansatz erzeugt eine fokussiertere Ablaufverfolgungsdatei, die nur den spezifischen Vorgang enthält, den Sie untersuchen.

Verstehen der Ablaufverfolgungsausgabe

Beim Erfassen einer Ablaufverfolgung mit dotnet-trace wird die Ausgabe ungefähr folgendermaßen angezeigt:

Process        : $HOME/.dotnet/tools/dotnet-dsrouter
Output File    : /tmp/hellomaui-app-trace
[00:00:00:35]    Recording trace 1.7997   (MB)
Press <Enter> or <Ctrl+C> to exit...

Nach dem Drücken <Enter>wird die Ablaufverfolgung abgeschlossen:

Stopping the trace. This may take up to minutes depending on the application being traced.

Trace completed.
Writing:    hellomaui-app-trace.speedscope.json

Anzeigen von Ablaufverfolgungsdateien

Das --format Argument steuert das Ausgabeformat:

  • nettrace (Standard): Kann in PerfView oder Visual Studio unter Windows angezeigt werden
  • speedscope: JSON-Format, das auf jeder Plattform angezeigt werden kann unter https://speedscope.app/

Für plattformübergreifende Analysen verwenden Sie --format speedscope.

dotnet-trace collect --dsrouter android --format speedscope

Profilerstellung unter Windows

Während das plattformübergreifende dotnet-trace Tool unter Windows funktioniert, bietet die Plattform zusätzliche native Profilerstellungsoptionen, die möglicherweise bequemer sind.

Verwenden von Visual Studio Performance Profiler

Der Visual Studio Performance Profiler bietet integrierte Profilerstellung für .NET-Anwendungen. Schauen Sie sich die Tour zur Visual Studio-Profilierung für umfassende Anleitungen an.

Verwenden von PerfView

PerfView ist ein leistungsstarkes, kostenloses Leistungsanalysetool für Windows, das .NET MAUI-Anwendungen mit minimalem Setup profiliert.

Um mit PerfView zu profilieren:

  1. Erstellen Sie Ihre Anwendung für Release mit aktiviertem ReadyToRun:

    dotnet publish -f net10.0-windows10.0.19041.0 -c Release -p:PublishReadyToRun=true
    
  2. Starten Sie PerfView, und wählen Sie .Collect>Collect

  3. Filtern Sie im Feld "Befehl" nach der ausführbaren Datei Ihrer App (z. B. hellomaui.exe).

  4. Klicken Sie auf "Sammlung starten", und starten Sie die App dann manuell.

  5. Klicken Sie auf "Sammlung beenden ", nachdem die App den Vorgang abgeschlossen hat, den Sie profilieren möchten.

  6. Öffnen Sie CPU-Stapel, um Zeitinformationen anzuzeigen, oder verwenden Sie die Registerkarte Flammendiagramm für eine grafische Ansicht.

Sie können die PerfView-Daten auch im SpeedScope-Format (File>Save View As) speichern, um sie https://speedscope.app/ für plattformübergreifende Analysen anzuzeigen.

Messen der Windows-Startzeit mit PerfView

Um genaue Startzeiten unter Windows zu messen, können Sie PerfView verwenden, um Event Tracing für Windows (ETW)-Ereignisse zu erfassen.

  1. Öffnen Collect>Collect und erweitern Sie in PerfView erweiterte Optionen.

  2. Konfigurieren Sie Folgendes:

    • Kernelbasis aktivieren
    • Zu Microsoft-Windows-XAML:0x44:Informational hinzufügen
  3. Klicken Sie auf „Sammlung starten“, öffnen und schließen Sie Ihre App 3-5 Mal.

  4. Klicken Sie auf "Sammlung beenden".

  5. Öffnen Sie den Ereignisbericht und berechnen Sie die Startzeit, indem Sie Folgendes finden:

    • Das Windows Kernel/Process/Start Ereignis für Ihre App (beachten Sie den Time MSec Wert)
    • Das erste Microsoft-Windows-XAML/Frame/Stop Ereignis für dieselbe Prozess-ID
    • Subtrahieren Sie die Startzeit von der Stoppzeit, um die Startdauer zu berechnen.

Führen Sie die App mehrmals aus und mitteln Sie die Ergebnisse, um genauere Messungen zu erhalten.

Verwenden von dotnet-trace unter Windows

Für entpackte Windows-Anwendungen können Sie folgendes direkt verwenden dotnet-trace :

dotnet publish -f net10.0-windows10.0.19041.0 -c Release -p:PublishReadyToRun=true -p:WindowsPackageType=None
dotnet trace collect --format speedscope -- bin\Release\net10.0-windows10.0.19041.0\win10-x64\publish\YourApp.exe

Profilerstellung auf iOS und Mac Catalyst mit Instrumenten

Für iOS- und Mac Catalyst-Apps bietet das Apple Instruments-Tool native Profilerstellung mit detaillierten Einblicken in die App-Startzeit und App-Leistung.

Verwenden von Instrumenten für die Profilerstellung für den App-Start

  1. Erstellen Sie Ihre App für Release, wobei die Symbole beibehalten werden:

    dotnet build -c Release -f net10.0-ios -p:NoSymbolStrip=true
    

    Die NoSymbolStrip=true Eigenschaft behält systemeigene Symbole in der ausführbaren Datei bei, wodurch Stack-Traces in Instruments deutlich hilfreicher werden.

  2. Installieren Sie die App auf Ihrem Gerät:

    dotnet build -t:Run -c Release -f net10.0-ios -p:NoSymbolStrip=true
    
  3. Starten Sie Instruments (in Xcode oder durch Ausführen von open -a Instruments im Terminal).

  4. Wählen Sie ihr iOS-Gerät oben aus.

  5. Wählen Sie Ihre App aus der Liste der installierten Anwendungen aus.

  6. Wählen Sie die App-Startinstrumentvorlage aus.

  7. Klicken Sie auf "Auswählen", und klicken Sie dann auf die Schaltfläche " Aufzeichnen ", um die Profilerstellung zu starten.

  8. Die App wird automatisch gestartet. Beenden Sie die Aufzeichnung, sobald die App vollständig gestartet wurde.

  9. Wählen Sie in den Ergebnissen die Zeile "App-Lebenszyklus " aus, um die Lebenszykluszeitachse anzuzeigen. In der letzten Zeile der unteren Tabelle zeigt Currently running in the foreground... den Zeitpunkt an, zu dem die App vollständig gestartet wurde (z. B.).

Weitere Informationen zur Verwendung von Instruments finden Sie in der Apple-Dokumentation zur Reduzierung der Startzeit Ihrer App.

Speicherverwendungsprofilierung

Die Speicherprofilerstellung hilft Ihnen, Speicherverluste zu identifizieren und Speicherzuordnungsmuster in Ihrer Anwendung zu verstehen. Verwenden Sie dotnet-gcdump, um Momentaufnahmen des verwalteten Speichers zu erstellen.

Sammeln von Speicherabbildern

Verwenden Sie zum Sammeln eines Speicherabbilds denselben --dsrouter Workflow wie dotnet-trace:

dotnet-gcdump collect --dsrouter android

Verwenden Sie --dsrouter android-emu, --dsrouter iosoder --dsrouter ios-sim für andere Ziele.

Im Gegensatz zur CPU-Ablaufverfolgung erfordern Speicherabbilder nicht das Anhalten des Anwendungsstarts. Erstellen Sie Ihre Anwendung mit -p:DiagnosticSuspend=false:

dotnet build -t:Run -c Release -f net10.0-android -p:DiagnosticAddress=127.0.0.1 -p:DiagnosticPort=9000 -p:DiagnosticSuspend=false -p:DiagnosticListenMode=connect

Sobald dotnet-gcdump eine Verbindung hergestellt wird, wird eine *.gcdump Datei im aktuellen Verzeichnis erstellt. Sie können diese Datei in Visual Studio unter Windows oder PerfView öffnen.

Analysieren von Speicherabbilder

Wenn Sie eine *.gcdump Datei in Visual Studio öffnen, können Sie:

  • Anzeigen aller verwalteten Objekte im Arbeitsspeicher
  • Die Gesamtanzahl und -größe jedes Typs anzeigen
  • Überprüfen des Referenzbaums, um zu verstehen, was Objekte am Leben hält
  • Vergleichen Sie mehrere Momentaufnahmen, um wachsende Allokationen zu identifizieren.

Mit dem Diagnosetool für die Speicherauslastung von Visual Studio (Debug>Windows>Diagnostic Tools) können Sie auch Momentaufnahmen beim Debuggen erstellen, sie sollten jedoch das erneute Laden von XAML deaktivieren, um genaue Ergebnisse zu erzielen.

Tipp

Erwägen Sie die Erstellung von Speichermomentaufnahmen von Release Builds, da Codepfade erheblich unterschiedlich sein können, wenn die XAML-Kompilierung, die AOT-Kompilierung und das Trimming aktiviert sind.

Diagnostizieren von Speicherlecks

Speicherverluste in .NET MAUI-Anwendungen manifestieren sich als stetig steigende Speicherauslastung, insbesondere während wiederholter Navigation oder Interaktionen. Auf mobilen Plattformen kann dies dazu führen, dass das Betriebssystem Ihre Anwendung aufgrund eines übermäßigen Arbeitsspeicherverbrauchs beendet.

Symptome von Speicherlecks

Ein typisches Symptom für einen Speicherverlust kann sein:

  1. Navigieren von der Hauptseite zu einer Detailseite
  2. Zurück navigieren
  3. Zur Detailseite erneut navigieren
  4. Der Speicher wächst mit jedem Zyklus konsistent.

Ermitteln, ob ein Leck vorhanden ist

Um festzustellen, ob eine Seite tatsächlich ausläuft, verwenden Sie Finalizer mit Protokollierung und erzwungener Garbage Collection (Speicherbereinigung) während des Debugging-Prozesses.

  1. Fügen Sie einen Finalizer mit Protokollierung zur Seitenklasse hinzu:

    ~MyDetailsPage() => System.Diagnostics.Debug.WriteLine("~MyDetailsPage() finalized");
    
  2. Erzwingen der Garbage Collection an strategischen Stellen (nur für das Debuggen):

    public MyDetailsPage()
    {
        GC.Collect(); // For debugging purposes only
        GC.WaitForPendingFinalizers();
        InitializeComponent();
    }
    
  3. Testen Sie einen Release Build, und beobachten Sie die Konsolenausgabe mithilfe von adb logcat (Android) oder Geräteprotokollen (iOS).

Wenn der Finalizer beim Verlassen der Seite ausgeführt wird, wird die Seite korrekt erfasst. Wenn der Finalizer niemals ausgeführt wird, tritt ein Speicherleck auf – etwas hält unbegrenzt einen Verweis darauf.

Warnung

Entfernen Sie GC.Collect() Aufrufe nach dem Debuggen. Sie dienen nur der Diagnose von Problemen und sollten niemals im Produktionscode enthalten sein.

Einschränken der Ursache

Nachdem Sie ein Leck identifiziert haben, schränken Sie die Ursache ein:

  1. Kommentieren Sie alle XAML-Inhalte aus. Tritt das Leck noch auf?
  2. Kommentieren Sie den gesamten C#-Code im CodeBehind aus. Tritt das Leck noch auf?
  3. Testen Sie auf mehreren Plattformen. Geschieht es nur auf einer Plattform?

Im Allgemeinen sollte ein leerer ContentPage Wert nicht geleert werden. Durch das systematische Entfernen von Code können Sie ermitteln, welches Steuerelement oder Codemuster das Problem verursacht.

Allgemeine Leckmuster

C#-Ereignisse

C#-Ereignisse können Zirkelverweise erstellen, die die Garbage Collection verhindern. Betrachten Sie ein Szenario, in dem ein untergeordnetes Objekt das Ereignis eines übergeordneten Elements abonniert, aber das übergeordnete Objekt auch einen Verweis auf das untergeordnete Element enthält. Beide Objekte können für immer leben.

Wenn die Ereignisquelle den Abonnent (z. B. ein Style In Application.Resources) überlebt, kann dies dazu führen, dass ganze Seiten verloren gehen.

Lösung: Verwenden Sie WeakEventManager für Ereignisse in .NET MAUI-Steuerelementen oder melden Sie sich von Ereignissen ab, wenn das Objekt nicht mehr benötigt wird.

iOS- und Mac Catalyst-Zirkelbezüge

Bei iOS und Mac Catalyst können Zirkelverweise zwischen C#-Objekten und nativen Objekten zu Lecks führen, da C#-Objekte, die Unterklassen NSObject sowohl in der Garbage-Collection .NET-Welt als auch in der referenzgezählten Objective-C-Welt vorhanden sind.

Beispiel für ein problematisches Muster:

class MyView : UIView
{
    public MyView()
    {
        var picker = new UIDatePicker();
        AddSubview(picker); // MyView -> UIDatePicker
        picker.ValueChanged += OnValueChanged; // UIDatePicker -> MyView via event handler
    }

    void OnValueChanged(object? sender, EventArgs e) { }
}

Lösungen:

  1. Erstellen von Ereignishandlern static:

    static void OnValueChanged(object? sender, EventArgs e) { }
    
  2. Verwenden Sie ein Proxyobjekt, das nicht von NSObject erbt.

    class MyView : UIView
    {
        readonly Proxy _proxy = new();
    
        public MyView()
        {
            var picker = new UIDatePicker();
            AddSubview(picker);
            picker.ValueChanged += _proxy.OnValueChanged;
        }
    
        class Proxy
        {
            public void OnValueChanged(object? sender, EventArgs e) { }
        }
    }
    

Hinweis

Diese Zirkelreferenzprobleme sind spezifisch für iOS und Mac Catalyst. Sie treten normalerweise nicht auf Android oder Windows auf.

Bewährte Methoden zum Vermeiden von Lecks

  • TestbuildsRelease: Das Speicherverhalten kann sich aufgrund von Optimierungen, Kürzen und AOT-Kompilierung erheblich von Debug Builds unterscheiden.

  • Verwenden Sie Finalizer bei der Untersuchung: Fügen Sie Finalizer mit Protokollierung zu wichtigen Objekten hinzu, um schnell zu ermitteln, ob sie gesammelt werden.

  • Von Ereignissen abmelden: Melden Sie sich immer von Ereignissen ab, wenn Objekte verworfen oder nicht mehr benötigt werden.

  • Seien Sie vorsichtig mit Ereignissen für langlebige Objekte: Vermeiden Sie es, dass langlebige Objekte (z. B. solche in Application.Resources) Verweise auf kurzlebige Objekte (z. B. Seiten oder Ansichten) halten.

  • Regelmäßiges Profiling: Machen Sie Speicherprofilierung zu einem Teil Ihres regelmäßigen Testprozesses, insbesondere nachdem Sie neue Features hinzugefügt oder erhebliche Änderungen vorgenommen haben.

Ausführlichere Informationen zu Speicherleckmustern und -techniken finden Sie im .NET MAUI Memory Leaks-Wiki.

Alternative Profilerstellungsansätze

Android ActivityManager-Startprotokolle

Android protokolliert automatisch Startzeitinformationen über den ActivityManager. Sie können diese Protokolle mithilfe von adb logcat:

adb logcat | grep "ActivityManager"

Wenn Ihre App gestartet wird, werden Nachrichten wie folgt angezeigt:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

Dies zeigt die Zeit an, die für die Anzeige Ihrer Aktivität benötigt wurde. Dies ist eine schnelle Möglichkeit, die Startzeit ohne zusätzliche Tools oder Codeänderungen zu messen.

Weitere Informationen zur Startzeit und Optimierungstechniken für Android-Apps finden Sie in der Android-Dokumentation zur Startzeit der App.

Loggingbasierte Startmessung

Um die Startzeit auf allen Plattformen zu messen, können Sie Nachrichten an bestimmten Stellen in Ihrer Anwendung protokollieren und die Zeit zwischen ihnen messen:

  1. Fügen Sie beim Laden der Hauptseite eine Protokollmeldung hinzu:

    Loaded += (sender, e) => Dispatcher.Dispatch(() => 
        Console.WriteLine("loaded"));
    
  2. Verwenden Sie ein Werkzeug wie das measure-startup Beispiel, um Ihre App zu starten und die Zeit zu messen, bis die Lognachricht angezeigt wird.

  3. Unter Android können Sie die Ausgabe adb logcat filtern, um bestimmte Nachrichten zu überwachen.

    adb logcat | grep "loaded"
    

Dieser Ansatz funktioniert auf allen Plattformen und ist nützlich für fortlaufende Integrationsszenarien oder Schnellprüfungen.

Zusätzliche Ressourcen