Laufzeiten und Kompilierung in .NET MAUI

.NET Multiplattform-App-UI (.NET MAUI) können je nach Zielplattform, Buildkonfiguration und Bereitstellungsmodell unterschiedliche Laufzeiten und Kompilierungsstrategien verwenden. In diesem Artikel werden die wichtigsten Begriffe sowie die Laufzeit- und Kompilierungsstrategien erklärt, die .NET MAUI für jede Plattform verwendet.

Laufzeiten

Eine .NET-Laufzeit ist die Ausführungsumgebung, die den Arbeitsspeicher, das Typsystem, die Garbage Collection und die Codeausführung Ihrer App verwaltet. .NET MAUI Apps verwenden eine der folgenden Laufzeiten:

Mono

Mono ist die plattformübergreifende .NET Laufzeit, die historisch Xamarin Apps und .NET MAUI-Apps unter Android, iOS und Mac Catalyst unterstützt. Mono unterstützt sowohl die Just-in-Time-Kompilierung (JIT) als auch die AOT-Kompilierung (Ahead-of-Time). Mono ist die Standardlaufzeit für .NET MAUI-Apps auf mobilen und Mac Catalyst-Plattformen.

Mono wird für .NET MAUI Apps, die auf .NET 11+ abzielen, nicht unterstützt.

Coreclr

CoreCLR ist die .NET Common Language Runtime, die von .NET auf Desktop- und Serverplattformen verwendet wird. Sie unterstützt ASP.NET Core, Konsolen-Apps und Windows Desktop-Apps. CoreCLR verfügt über einen stark optimierten JIT-Compiler, eine gestaffelte Kompilierung und einen vollständigen Satz von Laufzeitdiagnosen.

In .NET MAUI ist CoreCLR die unter Windows verwendete Laufzeit. In .NET 10 ist CoreCLR auch als experimentelle Option für Android verfügbar.

In .NET 11+ ist CoreCLR die Standardlaufzeit für .NET MAUI Apps auf Windows, Android, iOS und Mac Catalyst. NativeAOT ist eine Opt-In-Alternative für die Veröffentlichung auf allen .NET 11-Zielen. NativeAOT-Unterstützung ist experimentell unter Android.

NativeAOT

Wenn Sie mit nativem AOT veröffentlichen, wird Ihre App nicht auf der CoreCLR-Laufzeit ausgeführt. Stattdessen wird sie auf einer minimalen NativeAOT-Laufzeit ausgeführt, die statisch mit Ihrer nativen Binärdatei verknüpft ist. Diese Laufzeit enthält einen Garbage Collector und ein Typsystem, jedoch keinen JIT-Compiler oder -Interpreter. Der gesamte Code wird zur Build-Zeit in nativen Maschinencode kompiliert.

Kompilierungsstrategien

Eine Kompilierungsstrategie bestimmt, wie Ihr C#-Code in Computercode umgewandelt wird, den der Prozessor ausführen kann. .NET MAUI verwendet je nach Laufzeit- und Bereitstellungsszenario mehrere Kompilierungsstrategien.

JIT-Kompilierung (Just-in-Time)

Die JIT-Kompilierung übersetzt Microsoft Intermediate Language (MSIL) zur Laufzeit in systemeigenen Computercode, da Methoden zum ersten Mal aufgerufen werden. JIT-Kompilierung unterstützt schnelle Buildbereitstellungs-Debugzyklen und Features wie "Bearbeiten" und "Weiter".

  • Verwendet von: CoreCLR auf Windows und experimentell unter Android; Mono in Android-Debugbuilds
  • Vorteile: Schnelle Buildzeiten, vollständige Laufzeitdiagnose und unterstützung der dynamischen Codegenerierung
  • Nachteile: Langsamerer Start, da Code zur Laufzeit auf dem Gerät kompiliert wird
  • Verwendet von: CoreCLR auf Windows und Android
  • Vorteile: Schnelle Buildzeiten, vollständige Laufzeitdiagnose und unterstützung der dynamischen Codegenerierung
  • Nachteile: Langsamerer Start, wenn Code nicht bereits von ReadyToRun kompiliert wird

Apple CoreCLR-Geräteziele verwenden nicht JIT, da Apple-Plattformeinschränkungen dynamisch generierten Code verhindern. Stattdessen verwenden sie den CoreCLR-Interpreter.

Mono AOT (Ahead-of-Time)-Kompilierung

Die Mono-AOT-Kompilierung vorkompiliert MSIL beim Erstellen mithilfe des AOT-Compilers von Mono in nativen Code. Dies ist der Standardkompilierungsmodus für .NET MAUI-Versionen, die auf iOS, Mac Catalyst und Android basieren.

  • Verwendet von: Mono-Runtime unter iOS, Mac Catalyst und Android
  • Vorteile: Schnellerer Start als JIT und erforderlich für Plattformen, die die dynamische Codegenerierung einschränken
  • Nachteile: Größere App-Größe als JIT-Only-Builds und eingeschränkte Unterstützung für einige dynamische Features

Mono AOT ist nicht identisch mit NativeAOT. Mit Mono AOT enthält Ihre App weiterhin die Mono-Laufzeit und kann optional den Mono-Interpreter für Code verwenden, der nicht AOT-kompiliert wurde. NativeAOT kompiliert den gesamten Code vorab und enthält keinen JIT oder Interpreter. Mono unterstützt auch einen Vollständigen AOT-Modus, in dem kein Dolmetscher oder JIT verfügbar ist. Full AOT wird standardmäßig für Mono Release-Builds unter iOS und Mac Catalyst verwendet.

NativeAOT (Native Ahead-of-Time)-Kompilierung

NativeAOT kompiliert Ihre gesamte App, alle Abhängigkeiten und eine minimale Laufzeitumgebung beim Buildvorgang zu einer nativen Binärdatei. Zur Laufzeit gibt es keinen JIT-Compiler oder -Interpreter. NativeAOT führt eine vollständige Kürzung und statische Analyse durch, die kleinere App-Größen und schnellere Startzeiten erzeugen kann, aber es werden mehr Einschränkungen für die Codemuster, die Sie verwenden können, festgelegt.

  • Verfügbar unter: .NET MAUI iOS- und Mac Catalyst-Apps, wenn die Veröffentlichung explizit aktiviert ist
  • Vorteile: Kleine App-Größe, schnelles Starten und eine einzelne native Binärdatei
  • Nachteile: Längere Erstellungszeiten, keine dynamische Codegenerierung oder dynamisches Laden, und der gesamte Code muss trim-safe und AOT-kompatibel sein.
  • MSBuild-Eigenschaft: <PublishAot>true</PublishAot>
  • Verfügbar für: Alle .NET MAUI Ziele, wenn die Veröffentlichung explizit aktiviert ist. NativeAOT-Unterstützung ist experimentell unter Android.
  • Vorteile: Kleine App-Größe, schnelles Starten und eine einzelne native Binärdatei
  • Nachteile: Längere Erstellungszeiten, keine dynamische Codegenerierung oder dynamisches Laden, und der gesamte Code muss trim-safe und AOT-kompatibel sein.
  • MSBuild-Eigenschaft: <PublishAot>true</PublishAot>

NativeAOT ist ein reines Veröffentlichungsbereitstellungsmodell. Verwenden Sie dotnet publish, um eine NativeAOT-App zu erstellen und zu validieren. Ein regulärer Debugbuild verwendet nativeAOT nicht.

Weitere Informationen finden Sie unter native AOT-Bereitstellung unter iOS und Mac Catalyst und native AOT-Bereitstellung.

ReadyToRun (R2R)

ReadyToRun ist eine Form der Vorabkompilierung für CoreCLR, die Assemblys in einem Format vorkompiliert, das sowohl die ursprüngliche MSIL als auch eine systemeigene Codedarstellung enthält. Beim Start kann die Laufzeit den vorkompilierten systemeigenen Code anstelle der Kompilierung von Methoden aus MSIL verwenden. Auf Plattformen, auf denen JIT verfügbar ist, kann die Laufzeit weiterhin JIT-Kompilierungsmethoden ausführen, die nicht vorkompiliert sind und häufig verwendete Methoden zur Laufzeit optimieren.

  • Verwendet von: CoreCLR auf Windows und CoreCLR Android-Builds, die sich für die experimentelle Laufzeit entscheiden
  • Vorteile: Verbesserte Startzeit bei gleichzeitiger Beibehaltung der JIT-Kompatibilität
  • Nachteile: Größere Assemblygrößen, da Assemblys sowohl MSIL als auch nativen Code enthalten
  • MSBuild-Eigenschaft: <PublishReadyToRun>true</PublishReadyToRun>

ReadyToRun ist standardmäßig für .NET MAUI Apps im Release Modus Windows und für Android-Apps aktiviert, die CoreCLR (experimentell) im Release Modus verwenden. Die R2R-Bilder sind in den .dll Dateien verpackt.

  • Verwendet von: CoreCLR auf Windows, Android, iOS und Mac Catalyst
  • Vorteile: Verbesserte Startzeit beim Beibehalten des geeigneten Laufzeitkompilierungsverhaltens
  • Nachteile: Größere Anwendungs- und Downloadgrößen

Für Android-Apps ist ReadyToRun in der Release Konfiguration standardmäßig aktiviert. Die Standardeinstellung ist „Composite Partial ReadyToRun“. Full ReadyToRun kann mit MauiEnableFullReadyToRun=trueaktiviert werden. Es kann die Leistung beim Starten oder zur Laufzeit verbessern, erhöht aber die Paketgröße. Messen Sie daher die Auswirkungen für Ihre App.

Für iOS- und Mac Catalyst-Apps wird composite partial ReadyToRun in Debug Builds und composite full ReadyToRun in Release Builds verwendet. Der CoreCLR-Interpreter ist immer aktiviert und führt Code aus, der nicht vorkompiliert ist, da diese Plattformen keine JIT-Kompilierung zulassen.

Composite R2R kompiliert Assemblies gemeinsam und ermöglicht dadurch assemblyübergreifende Optimierungen. Teilweise R2R prekompiliert ausgewählte Methoden beim Verlassen der verbleibenden Methoden für die Laufzeitkompilierung, während vollständige R2R weitere Methoden vorkompiliert, aber nicht garantiert, dass jede Methode vorkompiliert ist. Methoden, die nicht vorkompiliert sind, werden je nach Plattform zur Laufzeit vom JIT oder Interpreter kompiliert.

Weitere Informationen finden Sie unter ReadyToRun-Kompilierung.

Profilgeführte Optimierung (PGO)

Profilgeführte Optimierung (Profile-Guided Optimization, PGO) verwendet Profilerstellungsdaten, um den Compiler auf die Erstellung effizienteren Codes zu lenken. Im Kontext von .NET MAUI gibt es zwei Formen von PGO:

  • Statisches PGO mit MIBC-Profilen: .NET MAUI enthält .mibc Profildateien, die Daten zu Methoden enthalten, die während des typischen Starts und der Verwendung von Apps häufig aufgerufen werden. Wenn die ReadyToRun-Kompilierung aktiviert ist, verwendet der R2R-Compiler diese Profile, um zu entscheiden, welche Methoden vorkompiliert werden sollen.
  • Dynamic PGO: Unter CoreCLR kann der JIT-Compiler zur Laufzeit Profildaten erfassen und verwenden, wenn Methoden mit weiteren Optimierungen neu kompiliert werden.

Weitere Informationen finden Sie unter Profilgeführte Optimierung und ReadyToRun-Kompilierung.

Mono-Interpreter

Der Mono-Interpreter ermöglicht es einer App, MSIL zur Laufzeit zu interpretieren, ohne systemeigenen Code dynamisch zu generieren. Es wird zusammen mit Mono AOT auf Plattformen verwendet, die keine JIT-Kompilierung zulassen, z. B. iOS-Geräte.

Der Mono-Interpreter ermöglicht auch .NET Hot Reload für Apps, die auf der Mono-Laufzeit ausgeführt werden. UseInterpreter ist auf Debug standardmäßig im true-Modus auf Android, iOS und Mac Catalyst eingestellt.

  • Verwendet von: Mono-Runtime
  • Vorteile: Unterstützt dynamische Features, die AOT nicht verarbeiten kann, ermöglicht .NET Hot Reload und kann die App-Größe verringern.
  • Nachteile: Interpretierter Code wird langsamer als kompilierter Code ausgeführt
  • MSBuild-Eigenschaft: <UseInterpreter>true</UseInterpreter>

Weitere Informationen finden Sie unter Interpreters unter iOS und Mac Catalyst.

CoreCLR-Dolmetscher

CoreCLR umfasst einen Dolmetscher unter iOS und Mac Catalyst. Der Interpreter ist immer aktiviert und führt Code aus, der nicht vorkompiliert ist, da diese Plattformen keine JIT-Kompilierung zulassen. Dieses Verhalten ist Teil der Laufzeit und erfordert keine MSBuild-Eigenschaft des Interpreters.

Was .NET MAUI standardmäßig verwendet

In der folgenden Tabelle wird zusammengefasst, welche Laufzeit- und Kompilierungsstrategie .NET MAUI auf der jeweiligen Plattform je nach Build-Konfiguration verwendet.

Plattform Debuggen Freigabe
Android Mono + JIT + Dolmetscher Mono + Mono AOT
Ios Mono + JIT (x64) / Mono + AOT + Interpreter (ARM64) Mono + Mono AOT
Mac Catalyst Mono + JIT (x64) / Mono + AOT + Interpreter (ARM64) Mono + Mono AOT
Windows CoreCLR + JIT CoreCLR + JIT + ReadyToRun

Hinweis

Wenn Sie sich für CoreCLR unter Android oder iOS entscheiden, indem Sie <UseMonoRuntime>false</UseMonoRuntime> in Ihrer Projektdatei festlegen, verwenden Builds standardmäßig "ReadyToRun". Unter iOS und Mac Catalyst mit CoreCLR ist composite ReadyToRun sowohl für Debug- als auch für Releasebuilds aktiviert.

Tipp

Sie können NativeAOT unter iOS und Mac Catalyst aktivieren, indem Sie <PublishAot>true</PublishAot> in Ihrer Projektdatei festlegen. NativeAOT wird nur bei der Veröffentlichung wirksam.

In der folgenden Tabelle sind die Laufzeit- und Kompilierungsstrategie zusammengefasst, die von .NET MAUI auf jeder Plattform verwendet wird:

Plattform Debuggen Freigabe
Android CoreCLR + JIT CoreCLR + composite partial ReadyToRun + JIT
Ios CoreCLR + teilweise zusammengesetztes ReadyToRun + Interpreter CoreCLR + composite full ReadyToRun + Interpreter
Mac Catalyst CoreCLR + teilweise zusammengesetztes ReadyToRun + Interpreter CoreCLR + composite full ReadyToRun + Interpreter
Windows CoreCLR + JIT CoreCLR + JIT + ReadyToRun

Important

Setzen Sie UseMonoRuntime nicht auf true, wenn Sie auf .NET 11+ abzielen. Mono wird nicht unterstützt, und das Festlegen dieser Eigenschaft erzeugt einen Buildfehler.

Tipp

NativeAOT wird mit <PublishAot>true</PublishAot> aktiviert und wird wirksam, wenn Sie dotnet publish verwenden. NativeAOT hat keinen JIT-Compiler und keinen Interpreter und erfordert vollständiges Trimmen.

Vergleich

In der folgenden Tabelle werden die kompilierungsstrategien verglichen, die für .NET MAUI-Apps verfügbar sind:

Strategie JIT Mono AOT ReadyToRun NativeAOT
Kompilierungszeit Zur Laufzeit Zur Buildzeit Zur Buildzeit Zur Buildzeit
Startgeschwindigkeit Langsamste Schnell Schnell Schnellste
Stetige Zustandsgeschwindigkeit Schnellstes (optimiertes JIT) Gut Schnellste (gestaffelte Neukompilierung) Gut
App-Größe Kleinste Größer Größer Kleinste
Dynamischer Code Vollständiger Support Begrenzt Vollständiger Support Nicht unterstützt
Kürzen erforderlich No Teilweise (Standardeinstellung) No Vollständig
Diagnostik Vollständig Begrenzt Vollständig Begrenzt

Typische Leistung

Die folgenden Benchmarks stammen aus einer dotnet new maui App und sind hardwareabhängig. Sie veranschaulichen relative Unterschiede zwischen Laufzeiten und sind keine absoluten Garantien.

Startzeit (Millisekunden):

Laufzeit Android Ios macOS
Mono (Standard) ~642 ~275 ~311
NativeAOT ~274 ~130 ~255

App-Größe unter iOS (MB):

Bereitstellungsmodell Größe
Mono (Standard) ~14,3
Mono (vollständiges Kürzen) ~11,5
NativeAOT ~5,4

In der folgenden Tabelle werden die Kompilierungsstrategien verglichen, die für .NET MAUI Apps für .NET 11 verfügbar sind:

Strategie CoreCLR JIT CoreCLR-Dolmetscher ReadyToRun NativeAOT
Kompilierungszeit Zur Laufzeit Zur Laufzeit Zur Buildzeit Zur Buildzeit
Startgeschwindigkeit Langsamer Langsamer Schnell Schnellste
App-Größe Kleiner Kleiner Größer Kleinste
Dynamischer Code Vollständiger Support Vollständiger Support Volle Unterstützung (entweder über JIT oder Dolmetscher) Nicht unterstützt
Diagnostik Vollständig Vollständig Begrenzt Begrenzt

CoreCLR unter Android und iOS

Ab .NET 10 können Sie sich für die Ausführung einer Android-App auf CoreCLR anstelle von Mono entscheiden.

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
    <UseMonoRuntime>false</UseMonoRuntime>
</PropertyGroup>

In .NET 10 ist CoreCLR unter Android ein experimentelles Feature und nicht für die Produktionsverwendung vorgesehen.

In .NET 11+ ist CoreCLR die Standardlaufzeit für Android-, iOS- und Mac Catalyst-Apps. NativeAOT ist eine Opt-In-Alternative, die CoreCLR nicht verwendet und auf Android experimentell ist. Nicht festlegen UseMonoRuntime=true; Mono wird für .NET 11+ nicht unterstützt, und das Festlegen der Eigenschaft erzeugt einen Buildfehler.

In einem Android APK

Die Inhalte eines APK unterscheiden sich je nach der von der App verwendeten Laufzeit- und Kompilierungsstrategie:

Mono:

  • classes.dex — Java/Kotlin-Code
  • lib/arm64-v8a/libmonosgen-2.0.so — Mono-Laufzeit
  • lib/arm64-v8a/libmonodroid.so — Android-spezifischer Runtime-Startkleber
  • lib/arm64-v8a/libassemblies.arm64-v8a.blob.so — Komprimierte, verpackte MSIL-Baugruppen
  • lib/arm64-v8a/libaot-*.dll.so — Mono AOT native Images in Releasebuilds

CoreCLR:

  • classes.dex — Java/Kotlin-Code
  • lib/arm64-v8a/libcoreclr.so — CoreCLR-Laufzeit
  • lib/arm64-v8a/libclrjit.so — CoreCLR JIT-Compiler
  • lib/arm64-v8a/libmonodroid.so — Android-spezifischer Runtime-Startkleber
  • lib/arm64-v8a/libassemblies.arm64-v8a.so — Verpacktes MSIL mit ReadyToRun-Bildern

Die Inhalte eines Android APK unterscheiden sich je nachdem, ob die App CoreCLR oder NativeAOT verwendet:

CoreCLR:

  • classes.dex — Java/Kotlin-Code
  • lib/arm64-v8a/libcoreclr.so — CoreCLR-Laufzeit
  • lib/arm64-v8a/libclrjit.so — CoreCLR JIT-Compiler
  • lib/arm64-v8a/libmonodroid.so — Android-spezifischer Runtime-Startkleber
  • lib/arm64-v8a/libassembly-store.so — Verpacktes MSIL mit ReadyToRun-Bildern

NativeAOT unter Android (experimentell):

  • classes.dex — Java/Kotlin-Code
  • lib/arm64-v8a/libhellomaui.so — Systemeigene Bibliothek mit Laufzeit, verwaltetem Code und Startkleber

Kürzen

Das Kürzen ist ein Buildschritt, in dem nicht verwendeter Code aus Ihrer App entfernt wird, um die Größe zu verringern. .NET MAUI verwendet den ILLink Trimmer, der Ihren Code analysiert und Typen, Methoden und Felder entfernt, auf die nicht statisch verwiesen wird.

Für CoreCLR-Release-Builds verwendet .NET MAUI standardmäßig TrimMode=partial, wodurch Framework-Assemblys, aber nicht Ihr Code oder NuGet-Referenzen, getrimmt werden. Legen Sie zum Verwenden des vollständigen Trimmings TrimMode auf full fest:

<PropertyGroup>
    <TrimMode>full</TrimMode>
</PropertyGroup>

NativeAOT führt automatisch die vollständige Kürzung durch. Legen Sie TrimMode bei Verwendung von NativeAOT nicht fest, um keinen anderen Trimmingmodus auszuwählen.

Weitere Informationen finden Sie unter Zum Kürzen einer .NET MAUI-App.

MSBuild-Eigenschaftsreferenz

In der folgenden Tabelle sind die MSBuild-Eigenschaften zusammengefasst, die das Laufzeit- und Kompilierungsverhalten steuern:

Eigentum Beschreibung Vorgabe
PublishAot NativeAOT-Kompilierung aktivieren. false
PublishReadyToRun Aktivieren Sie die ReadyToRun-Vorkompilierung für CoreCLR. true(Windows Release und anwendbare CoreCLR-Builds)
PublishTrimmed ILLink-Trimmen aktivieren. true (Veröffentlichung)
TrimMode Einstellung der Trimmaggressivität (partial oder full). partial
UseInterpreter Aktivieren Sie den Mono-Dolmetscher. true (iOS/Mac Catalyst Debug)
UseMonoRuntime Verwenden Sie Mono anstelle von CoreCLR. true auf unterstützten Mono-Zielen

In der folgenden Tabelle sind die MSBuild-Eigenschaften zusammengefasst, die für .NET 11 Laufzeit- und Kompilierungsverhalten relevant sind:

Eigentum Beschreibung Vorgabe
PublishAot Aktivieren Sie die NativeAOT-Kompilierung während der dotnet publish. false
MauiEnableFullReadyToRun Aktivieren Sie vollständige ReadyToRun für Android CoreCLR-Apps. false
TrimMode Legen Sie die Aggressivität des Trimmings für Builds ohne NativeAOT fest. partial

Siehe auch