Freigeben über


Anwendungsdomänen

Hinweis

Dieser Artikel ist spezifisch für .NET Framework. Sie gilt nicht für neuere Implementierungen von .NET, einschließlich .NET 6 und höherer Versionen.

Betriebssysteme und Laufzeitumgebungen bieten in der Regel eine Form der Isolierung zwischen Anwendungen. Beispielsweise verwendet Windows Prozesse zum Isolieren von Anwendungen. Diese Isolation ist erforderlich, um sicherzustellen, dass code, der in einer Anwendung ausgeführt wird, keine nachteiligen Auswirkungen auf andere, nicht verbundene Anwendungen hat.

Anwendungsdomänen bieten eine Isolationsgrenze für Sicherheit, Zuverlässigkeit und Versionsverwaltung sowie für das Entladen von Assemblys. Anwendungsdomänen werden in der Regel von Laufzeithosts erstellt, die für das Bootstrapping der Common Language Runtime verantwortlich sind, bevor eine Anwendung ausgeführt wird.

Die Vorteile der Isolierung von Anwendungen

In der Vergangenheit wurden Prozessgrenzen verwendet, um Anwendungen zu isolieren, die auf demselben Computer ausgeführt werden. Jede Anwendung wird in einen separaten Prozess geladen, der die Anwendung von anderen Anwendungen isoliert, die auf demselben Computer ausgeführt werden.

Die Anwendungen sind isoliert, da Speicheradressen prozessrelativ sind; Ein Speicherzeiger, der von einem Prozess an einen anderen übergeben wird, kann im Zielprozess nicht sinnvoll verwendet werden. Darüber hinaus können Sie keine direkten Aufrufe zwischen zwei Prozessen ausführen. Stattdessen müssen Sie Proxys verwenden, die eine Indirektionsebene bereitstellen.

Verwalteter Code muss über einen Überprüfungsprozess übergeben werden, bevor er ausgeführt werden kann (es sei denn, der Administrator hat die Berechtigung zum Überspringen der Überprüfung erteilt). Der Überprüfungsprozess bestimmt, ob der Code versuchen kann, auf ungültige Speicheradressen zuzugreifen, oder eine andere Aktion ausführen kann, die dazu führen kann, dass der Prozess, in dem er ausgeführt wird, nicht ordnungsgemäß ausgeführt wird. Code, der den Überprüfungstest bestanden hat, wird als typsicher bezeichnet. Die Möglichkeit, Code als typsicher zu überprüfen, ermöglicht es der Common Language Runtime, eine möglichst hohe Isolationsstufe wie die Prozessgrenze zu einem erheblich geringeren Leistungsaufwand bereitzustellen.

Anwendungsdomänen bieten eine sicherere und vielseitigere Verarbeitungseinheit, mit der die Common Language Runtime die Isolation zwischen Anwendungen bereitstellen kann. Sie können mehrere Anwendungsdomänen in einem einzelnen Prozess mit der gleichen Isolationsebene ausführen, die in separaten Prozessen vorhanden wäre, aber ohne dass der zusätzliche Aufwand für prozessübergreifende Aufrufe oder das Wechseln zwischen Prozessen entsteht. Die Möglichkeit, mehrere Anwendungen innerhalb eines einzelnen Prozesses auszuführen, erhöht die Serverskalierbarkeit erheblich.

Das Isolieren von Anwendungen ist auch für die Anwendungssicherheit wichtig. So können Sie beispielsweise Steuerelemente aus mehreren Webanwendungen in einem einzigen Browserprozess ausführen, sodass die Steuerelemente nicht auf die Daten und Ressourcen der anderen zugreifen können.

Die von Anwendungsdomänen bereitgestellte Isolation hat die folgenden Vorteile:

  • Fehler in einer Anwendung können sich nicht auf andere Anwendungen auswirken. Da typsicherer Code keine Speicherfehler verursachen kann, stellt die Verwendung von Anwendungsdomänen sicher, dass code, der in einer Domäne ausgeführt wird, keine Auswirkungen auf andere Anwendungen im Prozess hat.

  • Einzelne Anwendungen können beendet werden, ohne den gesamten Prozess zu beenden. Mithilfe von Anwendungsdomänen können Sie den Code entladen, der in einer einzigen Anwendung ausgeführt wird.

    Hinweis

    Einzelne Assemblys oder Typen können nicht entladen werden. Es kann nur eine vollständige Domäne entladen werden.

  • Code, der in einer Anwendung ausgeführt wird, kann nicht direkt von einer anderen Anwendung aus auf Code oder Ressourcen zugreifen. Die Common Language Runtime erzwingt diese Isolation, indem direkte Aufrufe zwischen Objekten in verschiedenen Anwendungsdomänen verhindert werden. Objekte, die zwischen Domänen übergeben werden, werden entweder kopiert oder vom Proxy darauf zugegriffen. Wenn das Objekt kopiert wird, ist der Aufruf des Objekts lokal. Das heißt, sowohl der Aufrufer als auch das Objekt, auf das verwiesen wird, befinden sich in derselben Anwendungsdomäne. Wenn auf das Objekt über einen Proxy zugegriffen wird, erfolgt der Aufruf des Objekts remote. In diesem Fall befinden sich der Aufrufer und das objekt, auf das verwiesen wird, in verschiedenen Anwendungsdomänen. Domänenübergreifende Aufrufe verwenden dieselbe Remoteanrufinfrastruktur wie Aufrufe zwischen zwei Prozessen oder zwischen zwei Computern. Daher müssen die Metadaten für das objekt, auf das verwiesen wird, für beide Anwendungsdomänen verfügbar sein, damit der Methodenaufruf ordnungsgemäß JIT-kompiliert werden kann. Wenn die aufrufende Domäne keinen Zugriff auf die Metadaten für das aufgerufene Objekt hat, schlägt die Kompilierung möglicherweise mit ausnahme des Typs FileNotFoundExceptionfehl. Weitere Informationen finden Sie unter Remoteobjekte. Der Mechanismus zum Bestimmen, wie auf Objekte über Domänen hinweg zugegriffen werden kann, wird durch das Objekt bestimmt. Weitere Informationen finden Sie unter System.MarshalByRefObject.

  • Das Verhalten von Code wird von der Anwendung, in der sie ausgeführt wird, festgelegt. Mit anderen Worten, die Anwendungsdomäne stellt Konfigurationseinstellungen wie Anwendungsversionsrichtlinien, den Speicherort aller Remoteassemblys bereit, auf die sie zugreift, und Informationen darüber, wo Assemblys gefunden werden, die in die Domäne geladen werden.

  • Berechtigungen, die dem Code erteilt werden, können von der Anwendungsdomäne gesteuert werden, in der der Code ausgeführt wird.

Anwendungsdomänen und Assemblies

In diesem Abschnitt wird die Beziehung zwischen Anwendungsdomänen und Assemblys beschrieben. Sie müssen eine Assembly in eine Anwendungsdomäne laden, bevor Sie den darin enthaltenen Code ausführen können. Das Ausführen einer typischen Anwendung bewirkt, dass mehrere Assemblys in eine Anwendungsdomäne geladen werden.

Die Art und Weise, wie eine Assembly geladen wird, bestimmt, ob der kompilierte JiT-Code (Just-in-Time) von mehreren Anwendungsdomänen im Prozess gemeinsam genutzt werden kann und ob die Assembly aus dem Prozess entladen werden kann.

  • Wenn eine Assembly domänenneutral geladen wird, können alle Anwendungsdomänen, die denselben Sicherheitserteilungssatz verwenden, denselben JIT-kompilierten Code gemeinsam nutzen, wodurch der von der Anwendung erforderliche Arbeitsspeicher reduziert wird. Die Baugruppe kann jedoch nie aus dem Prozess entladen werden.

  • Wenn eine Assembly nicht domänenneutral geladen wird, muss sie in jeder Anwendungsdomäne, in der sie geladen wird, JIT-kompiliert werden. Die Assembly kann jedoch aus dem Prozess entladen werden, indem alle Anwendungsdomänen entladen werden, in denen sie geladen wird.

Der Laufzeithost bestimmt, ob Assemblys als domänenneutral geladen werden sollen, wenn er die Laufzeit in einen Prozess lädt. Wenden Sie für verwaltete Anwendungen das LoaderOptimizationAttribute Attribut auf die Einstiegspunktmethode für den Prozess an, und geben Sie einen Wert aus der zugehörigen LoaderOptimization Enumeration an. Geben Sie für nicht verwaltete Anwendungen, die die Common Language Runtime hosten, das entsprechende Flag an, wenn Sie die CorBindToRuntimeEx Function-Methode aufrufen.

Es gibt drei Optionen zum Laden domänenneutraler Assemblys:

  • LoaderOptimization.SingleDomain – Es werden keine Assemblys als domänenneutral geladen, mit Ausnahme von Mscorlib, die immer domänenneutral geladen wird. Diese Einstellung wird als einzelne Domäne bezeichnet, da sie häufig verwendet wird, wenn der Host nur eine einzelne Anwendung im Prozess ausführt.

  • LoaderOptimization.MultiDomain – Alle Assemblys werden als domänenneutral geladen. Verwenden Sie diese Einstellung, wenn mehrere Anwendungsdomänen im Prozess vorhanden sind, die alle denselben Code ausführen.

  • LoaderOptimization.MultiDomainHost – Assemblys mit starkem Namen werden als domänenneutral geladen, wenn sie und alle zugehörigen Abhängigkeiten im globalen Assemblycache installiert worden sind. Andere Assemblys werden für jede Anwendungsdomäne, in der sie geladen werden, separat geladen und JIT-kompiliert und können daher aus dem Prozess entladen werden. Verwenden Sie diese Einstellung, wenn mehrere Anwendungen im selben Prozess ausgeführt werden, oder wenn Sie über eine Mischung aus Assemblys verfügen, die von vielen Anwendungsdomänen und Assemblys gemeinsam verwendet werden, die aus dem Prozess entladen werden müssen.

JIT-kompilierter Code kann nicht gemeinsam von Assemblys genutzt werden, die mit der LoadFrom-Methode der Assembly-Klasse in den LoadFrom-Kontext geladen werden oder die aus Abbildern mithilfe von Überladungen der Load-Methode geladen werden, die Bytearrays angeben.

Assemblys, die mithilfe des Ngen.exe (Native Image Generator) in nativen Code kompiliert wurden, können zwischen Anwendungsdomänen gemeinsam genutzt werden, wenn sie bei ihrem ersten Laden als domänenneutral in einen Prozess geladen werden.

JIT-kompilierter Code für die Assembly, die den Anwendungseinstiegspunkt enthält, wird nur freigegeben, wenn alle abhängigkeiten freigegeben werden können.

Eine domänenneutrale Assembly kann mehrmals JIT-kompiliert werden. Wenn beispielsweise die Sicherheitserteilungssätze von zwei Anwendungsdomänen unterschiedlich sind, können sie nicht denselben JIT-kompilierten Code gemeinsam nutzen. Jede Kopie der JIT-kompilierten Assembly kann jedoch für andere Anwendungsdomänen freigegeben werden, die denselben Grant-Satz aufweisen.

Wenn Sie entscheiden, ob Assemblys als domänenneutral geladen werden sollen, müssen Sie einen Kompromiss zwischen der Reduzierung der Speichernutzung und anderen Leistungsfaktoren treffen.

  • Der Zugriff auf statische Daten und Methoden ist für domänenneutrale Assemblys aufgrund der Notwendigkeit, Assemblys zu isolieren, langsamer. Jede Anwendungsdomäne, die auf die Assembly zugreift, muss über eine separate Kopie der statischen Daten verfügen, um zu verhindern, dass Verweise auf Objekte in statischen Feldern Domänengrenzen überschreiten. Daher enthält die Laufzeit zusätzliche Logik, um einen Aufrufer an die entsprechende Kopie der statischen Daten oder Methode zu leiten. Durch diese zusätzliche Logik wird der Aufruf verlangsamt.

  • Alle Abhängigkeiten einer Assembly müssen gefunden und geladen werden, wenn die Assembly domänenneutral geladen wird, da eine Abhängigkeit, die nicht domänenneutral geladen werden kann, verhindert, dass die Assembly domänenneutral geladen wird.

Anwendungsdomänen und Threads

Eine Anwendungsdomäne bildet eine Isolationsgrenze für Sicherheit, Versionsverwaltung, Zuverlässigkeit und Entladung von verwaltetem Code. Ein Thread ist das Betriebssystemkonstrukt, das von der Common Language Runtime zum Ausführen von Code verwendet wird. Zur Laufzeit wird der gesamte verwaltete Code in eine Anwendungsdomäne geladen und von mindestens einem verwalteten Threads ausgeführt.

Es gibt keine 1:1-Korrelation zwischen Anwendungsdomänen und Threads. Mehrere Threads können zu einem bestimmten Zeitpunkt in einer einzelnen Anwendungsdomäne ausgeführt werden, und ein bestimmter Thread ist nicht auf eine einzelne Anwendungsdomäne beschränkt. Das heißt, Threads sind frei für anwendungsübergreifende Domänengrenzen; Für jede Anwendungsdomäne wird kein neuer Thread erstellt.

Zu einem bestimmten Zeitpunkt wird jeder Thread in einer Anwendungsdomäne ausgeführt. Null, ein oder mehrere Threads können in einer bestimmten Anwendungsdomäne ausgeführt werden. Die Laufzeit verfolgt, welche Threads in welchen Anwendungsdomänen ausgeführt werden. Sie können die Domäne suchen, in der ein Thread jederzeit ausgeführt wird, indem Sie die Thread.GetDomain Methode aufrufen.

Anwendungsdomänen und -kulturen

Kultur, die durch ein CultureInfo Objekt dargestellt wird, ist Threads zugeordnet. Sie können die Kultur abrufen, die dem derzeit ausgeführten Thread zugeordnet ist, indem Sie die CultureInfo.CurrentCulture Eigenschaft verwenden, und Sie können die Kultur abrufen oder festlegen, die dem derzeit ausgeführten Thread zugeordnet ist, indem Sie die Thread.CurrentCulture Eigenschaft verwenden. Wenn die Kultur, die einem Thread zugeordnet ist, explizit mithilfe der Thread.CurrentCulture Eigenschaft festgelegt wurde, wird sie weiterhin diesem Thread zugeordnet, wenn der Thread Anwendungsdomänengrenzen überschreitet. Andernfalls wird die Kultur, die dem Thread zu einem bestimmten Zeitpunkt zugeordnet ist, durch den Wert der Eigenschaft in der CultureInfo.DefaultThreadCurrentCulture Anwendungsdomäne bestimmt, in der der Thread ausgeführt wird:

  • Wenn der Wert der Eigenschaft nicht null lautet, wird die Kultur, die von der Eigenschaft zurückgegeben wird, dem Thread zugeordnet (und daher von den Thread.CurrentCulture- und CultureInfo.CurrentCulture-Eigenschaften zurückgegeben).

  • Wenn der Wert der Eigenschaft lautet, wird nulldie aktuelle Systemkultur dem Thread zugeordnet.

Programmieren mit Anwendungsdomänen

Anwendungsdomänen werden in der Regel programmgesteuert von Laufzeithosts erstellt und bearbeitet. Manchmal möchte ein Anwendungsprogramm jedoch auch mit Anwendungsdomänen arbeiten. Ein Anwendungsprogramm könnte beispielsweise eine Anwendungskomponente in eine Domäne laden, um die Domäne (und die Komponente) entladen zu können, ohne die gesamte Anwendung beenden zu müssen.

Dieses AppDomain ist die programmgesteuerte Schnittstelle zu Anwendungsdomänen. Diese Klasse enthält Methoden zum Erstellen und Entladen von Domänen, zum Erstellen von Instanzen von Typen in Domänen und zum Registrieren für verschiedene Benachrichtigungen wie das Entladen von Anwendungsdomänen. In der folgenden Tabelle sind häufig verwendete Methoden aufgeführt AppDomain .

AppDomain-Methode BESCHREIBUNG
CreateDomain Erstellt eine neue Anwendungsdomäne. Es wird empfohlen, dass Sie eine Überladung dieser Methode verwenden, die ein AppDomainSetup-Objekt angibt. Dies ist die bevorzugte Methode zum Festlegen der Eigenschaften einer neuen Domäne, z. B. der Anwendungsbasis oder des Stammverzeichnisses für die Anwendung; der Speicherort der Konfigurationsdatei für die Domäne; und der Suchpfad, den die Common Language Runtime zum Laden von Assemblys in die Domäne verwendet.
ExecuteAssembly und ExecuteAssemblyByName Führt eine Assembly in der Anwendungsdomäne aus. Dies ist eine Instanzmethode, sodass sie zum Ausführen von Code in einer anderen Anwendungsdomäne verwendet werden kann, auf die Sie einen Verweis haben.
CreateInstanceAndUnwrap Erstellt eine Instanz eines angegebenen Typs in der Anwendungsdomäne und gibt einen Proxy zurück. Verwenden Sie diese Methode, um zu vermeiden, dass die Assembly, die den erstellen Typ enthält, in die aufrufende Assembly geladen wird.
Unload Fährt die Domäne ordnungsgemäß herunter. Die Anwendungsdomäne wird erst entladen, wenn alle threads, die in der Domäne ausgeführt werden, entweder beendet wurden oder sich nicht mehr in der Domäne befinden.

Hinweis

Die Common Language Runtime unterstützt keine Serialisierung globaler Methoden, sodass Stellvertretungen nicht zum Ausführen globaler Methoden in anderen Anwendungsdomänen verwendet werden können.

Die nicht verwalteten Schnittstellen, die in der Spezifikation der Hostingschnittstellen für gemeinsame Sprachen beschrieben sind, bieten auch Zugriff auf Anwendungsdomänen. Laufzeithosts können Schnittstellen aus nicht verwaltetem Code verwenden, um Anwendungsdomänen innerhalb eines Prozesses zu erstellen und Zugriff auf diese zu erhalten.

Die Umgebungsvariable COMPLUS_LoaderOptimization

Eine Umgebungsvariable, die die Standardrichtlinie für die Ladeprogrammoptimierung einer ausführbaren Anwendung festlegt.

Syntax

COMPLUS_LoaderOptimization = 1

Bemerkungen

Eine typische Anwendung lädt mehrere Assemblys in eine Anwendungsdomäne, bevor der enthaltene Code ausgeführt werden kann.

Die Art und Weise, wie die Assembly geladen wird, bestimmt, ob der kompilierte JIT-Code (Just-in-Time) von mehreren Anwendungsdomänen im Prozess gemeinsam genutzt werden kann.

  • Wenn eine Assembly domänenneutral geladen wird, können alle Anwendungsdomänen, die denselben Sicherheitserteilungssatz verwenden, denselben JIT-kompilierten Code verwenden. Dadurch wird der von der Anwendung erforderliche Arbeitsspeicher reduziert.

  • Wenn eine Assembly nicht domänenneutral geladen wird, muss sie in jeder Anwendungsdomäne, in der sie geladen wird, JIT kompiliert werden, und das Ladeprogramm darf interne Ressourcen nicht über Anwendungsdomänen hinweg freigeben.

Wenn dieser Wert auf 1 gesetzt ist, zwingt das COMPLUS_LoaderOptimizations-Umgebungsflag den Laufzeithost, alle Assemblys auf nicht domänenneutrale Weise zu laden, die als SingleDomain bezeichnet wird. SingleDomain lädt keine Assemblys als domänenneutral, mit Ausnahme von Mscorlib, die immer domänenneutral geladen wird. Diese Einstellung wird als einzelne Domäne bezeichnet, da sie häufig verwendet wird, wenn der Host nur eine einzelne Anwendung im Prozess ausführt.

Vorsicht

Das COMPLUS_LoaderOptimization-Umgebungs-Flag wurde für Diagnose- und Testszenarien entwickelt. Wenn das Flag aktiviert ist, kann dies zu einer starken Verlangsamung und einer Erhöhung der Arbeitsspeicherauslastung führen.

Codebeispiel

Durch den Anhang von COMPLUS_LoaderOptimization=1 an den Multi-Zeichenfolgen-Wert der Umgebung im HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN-Schlüssel kann erzwungen werden, dass keine Assemblys für den IISADMIN-Dienst als domänenneutral geladen werden.

Key = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN
Name = Environment
Type = REG_MULTI_SZ
Value (to append) = COMPLUS_LoaderOptimization=1

Siehe auch