注
この記事は .NET Framework に固有のものです。 .NET 6 以降のバージョンを含む、.NET の新しい実装には適用されません。
通常、オペレーティング システムとランタイム環境では、アプリケーション間で何らかの形式の分離が提供されます。 たとえば、Windows ではプロセスを使用してアプリケーションを分離します。 この分離は、1 つのアプリケーションで実行されているコードが、関連のない他のアプリケーションに悪影響を与えないようにするために必要です。
アプリケーション ドメインは、セキュリティ、信頼性、バージョン管理、およびアセンブリのアンロードのための分離境界を提供します。 通常、アプリケーション ドメインはランタイム ホストによって作成されます。ランタイム ホストは、アプリケーションを実行する前に共通言語ランタイムをブートストラップします。
アプリケーションを分離する利点
これまで、プロセス境界は、同じコンピューター上で実行されているアプリケーションを分離するために使用されてきました。 各アプリケーションは個別のプロセスに読み込まれ、同じコンピューター上で実行されている他のアプリケーションからアプリケーションが分離されます。
メモリ アドレスはプロセス相対であるため、アプリケーションは分離されます。あるプロセスから別のプロセスに渡されたメモリ ポインターは、ターゲット プロセスで意味のある方法では使用できません。 さらに、2 つのプロセス間で直接呼び出しを行うことはできません。 代わりに、間接参照のレベルを提供するプロキシを使用する必要があります。
マネージド コードは、検証プロセスを実行する前に渡す必要があります (管理者が検証をスキップするアクセス許可を付与していない場合)。 検証プロセスは、コードが無効なメモリ アドレスへのアクセスを試みることができるか、または実行されているプロセスが正常に動作しない原因となる可能性のある他のアクションを実行できるかどうかを決定します。 検証テストに合格したコードは、タイプ セーフと言われます。 コードをタイプ セーフとして検証する機能により、共通言語ランタイムは、プロセスの境界と同じくらい優れたレベルの分離を実現し、パフォーマンス コストを大幅に削減できます。
アプリケーション ドメインは、共通言語ランタイムがアプリケーション間の分離を提供するために使用できる、より安全で汎用性の高い処理単位を提供します。 複数のアプリケーション ドメインは、個別のプロセスに存在するのと同じレベルの分離を使用して 1 つのプロセスで実行できますが、プロセス間の呼び出しやプロセス間の切り替えによる追加のオーバーヘッドは発生しません。 1 つのプロセス内で複数のアプリケーションを実行する機能により、サーバーのスケーラビリティが大幅に向上します。
アプリケーションの分離は、アプリケーションのセキュリティにも重要です。 たとえば、コントロールが互いのデータとリソースにアクセスできないように、1 つのブラウザー プロセスで複数の Web アプリケーションからコントロールを実行できます。
アプリケーション ドメインによって提供される分離には、次の利点があります。
1 つのアプリケーションの障害が他のアプリケーションに影響を与えることはできません。 タイプ セーフなコードはメモリ エラーを引き起こすことができないため、アプリケーション ドメインを使用すると、1 つのドメインで実行されているコードがプロセス内の他のアプリケーションに影響を与えないようにすることができます。
プロセス全体を停止することなく、個々のアプリケーションを停止できます。 アプリケーション ドメインを使用すると、1 つのアプリケーションで実行されているコードをアンロードできます。
注
個々のアセンブリまたは型をアンロードすることはできません。 完全なドメインのみをアンロードできます。
あるアプリケーションで実行されているコードは、別のアプリケーションのコードまたはリソースに直接アクセスできません。 共通言語ランタイムは、異なるアプリケーション ドメイン内のオブジェクト間の直接呼び出しを防ぐことで、この分離を強制します。 ドメイン間で渡されるオブジェクトは、プロキシによってコピーまたはアクセスされます。 オブジェクトがコピーされた場合、オブジェクトの呼び出しはローカルです。 つまり、呼び出し元と参照されているオブジェクトの両方が同じアプリケーション ドメイン内にあります。 プロキシを介してオブジェクトにアクセスする場合、オブジェクトの呼び出しはリモートです。 この場合、呼び出し元と参照されるオブジェクトは、異なるアプリケーション ドメインにあります。 クロスドメイン呼び出しでは、2 つのプロセス間または 2 台のマシン間の呼び出しと同じリモート呼び出しインフラストラクチャが使用されます。 そのため、メソッド呼び出しを適切に JIT コンパイルできるようにするには、参照されるオブジェクトのメタデータを両方のアプリケーション ドメインで使用できる必要があります。 呼び出し元ドメインが呼び出されるオブジェクトのメタデータにアクセスできない場合は、 FileNotFoundException型を除き、コンパイルが失敗する可能性があります。 詳細については、「 リモート オブジェクト」を参照してください。 ドメイン間でオブジェクトにアクセスする方法を決定するメカニズムは、オブジェクトによって決まります。 詳細については、System.MarshalByRefObjectを参照してください。
コードの動作のスコープは、コードを実行するアプリケーションによって行われます。 つまり、アプリケーション ドメインには、アプリケーション バージョン ポリシー、アクセスするリモート アセンブリの場所、ドメインに読み込まれるアセンブリの場所に関する情報などの構成設定が用意されています。
コードに付与されるアクセス許可は、コードが実行されているアプリケーション ドメインによって制御できます。
アプリケーション ドメインとアセンブリ
このセクションでは、アプリケーション ドメインとアセンブリ間の関係について説明します。 アセンブリに含まれるコードを実行するには、アセンブリをアプリケーション ドメインに読み込む必要があります。 一般的なアプリケーションを実行すると、複数のアセンブリがアプリケーション ドメインに読み込まれます。
アセンブリの読み込み方法によって、Just-In-Time (JIT) コンパイル済みコードをプロセス内の複数のアプリケーション ドメインで共有できるかどうか、およびアセンブリをプロセスからアンロードできるかどうかが決まります。
アセンブリがドメインに依存しない状態で読み込まれる場合、同じセキュリティ許可セットを共有するすべてのアプリケーション ドメインで同じ JIT コンパイル コードを共有できるため、アプリケーションに必要なメモリが削減されます。 ただし、アセンブリをプロセスからアンロードすることはできません。
ドメインに依存しないアセンブリが読み込まれていない場合は、読み込まれるすべてのアプリケーション ドメインで JIT コンパイルする必要があります。 ただし、アセンブリは、読み込まれるすべてのアプリケーション ドメインをアンロードすることで、プロセスからアンロードできます。
ランタイム ホストは、ランタイムをプロセスに読み込むときに、アセンブリをドメイン中立として読み込むかどうかを決定します。 マネージド アプリケーションの場合は、プロセスのエントリ ポイント メソッドに LoaderOptimizationAttribute 属性を適用し、関連付けられている LoaderOptimization 列挙体の値を指定します。 共通言語ランタイムをホストするアンマネージ アプリケーションの場合は、 CorBindToRuntimeEx 関数 メソッドを呼び出すときに適切なフラグを指定します。
ドメインに依存しないアセンブリを読み込むには、次の 3 つのオプションがあります。
LoaderOptimization.SingleDomain は、常にドメインニュートラルで読み込まれる Mscorlib を除き、アセンブリをドメインニュートラルとして読み込まない。 この設定は、ホストがプロセス内で 1 つのアプリケーションのみを実行している場合に一般的に使用されるため、単一ドメインと呼ばれます。
LoaderOptimization.MultiDomain は、すべてのアセンブリをドメインニュートラルとして読み込みます。 この設定は、プロセスに複数のアプリケーション ドメインがあり、そのすべてが同じコードを実行する場合に使用します。
LoaderOptimization.MultiDomainHost は、厳密に名前付けされたアセンブリとそのすべての依存関係がグローバル アセンブリ キャッシュにインストールされている場合、ドメイン中立としてアセンブリを読み込みます。 他のアセンブリは、読み込まれるアプリケーション ドメインごとに個別に読み込まれ、JIT コンパイルされるため、プロセスからアンロードできます。 この設定は、同じプロセスで複数のアプリケーションを実行する場合、またはプロセスからアンロードする必要がある多数のアプリケーション ドメインとアセンブリで共有されるアセンブリの組み合わせがある場合に使用します。
JIT コンパイル コードは、読み込み元コンテキストに読み込まれたアセンブリ、LoadFrom クラスのAssembly メソッドを使用するアセンブリ、またはバイト配列を指定するLoad メソッドのオーバーロードを使用してイメージから読み込む場合は共有できません。
Ngen.exe (ネイティブ イメージ ジェネレーター) を使用してネイティブ コードにコンパイルされたアセンブリは、プロセスに初めて読み込まれる際にドメインに依存しない状態で読み込まれる場合、アプリケーション ドメイン間で共有できます。
アプリケーション エントリ ポイントを含むアセンブリの JIT コンパイル コードは、すべての依存関係を共有できる場合にのみ共有されます。
ドメインに依存しないアセンブリは、JIT コンパイルを複数回実行できます。 たとえば、2 つのアプリケーション ドメインのセキュリティ許可セットが異なる場合、同じ JIT コンパイル コードを共有することはできません。 ただし、JIT コンパイル アセンブリの各コピーは、同じ許可セットを持つ他のアプリケーション ドメインと共有できます。
アセンブリをドメインに依存しないものとして読み込むかどうかを決定する場合は、メモリ使用量の削減とその他のパフォーマンス要因とのトレードオフを行う必要があります。
アセンブリを分離する必要があるため、ドメインに依存しないアセンブリでは静的データとメソッドへのアクセスが遅くなります。 アセンブリにアクセスする各アプリケーション ドメインには、静的フィールド内のオブジェクトへの参照がドメイン境界を越えないように、静的データの個別のコピーが必要です。 その結果、ランタイムには、静的データまたはメソッドの適切なコピーに呼び出し元を誘導する追加のロジックが含まれています。 この追加ロジックにより、呼び出しが遅くなります。
ドメインニュートラルを読み込むことができない依存関係では、アセンブリがドメインニュートラルに読み込まれないため、アセンブリがドメインニュートラルで読み込まれるときに、アセンブリのすべての依存関係を見つけて読み込む必要があります。
アプリケーション ドメインとスレッド
アプリケーション ドメインは、マネージド コードのセキュリティ、バージョン管理、信頼性、アンロードのための分離境界を形成します。 スレッドは、コードを実行するために共通言語ランタイムによって使用されるオペレーティング システムコンストラクトです。 実行時に、すべてのマネージド コードがアプリケーション ドメインに読み込まれ、1 つ以上のマネージド スレッドによって実行されます。
アプリケーション ドメインとスレッドの間に 1 対 1 の相関関係はありません。 特定の時点で複数のスレッドを 1 つのアプリケーション ドメインで実行でき、特定のスレッドは 1 つのアプリケーション ドメインに限定されません。 つまり、スレッドは自由にアプリケーション ドメインの境界を越えることができます。アプリケーション ドメインごとに新しいスレッドは作成されません。
任意の時点で、すべてのスレッドがアプリケーション ドメインで実行されます。 0、1、または複数のスレッドが任意のアプリケーション ドメインで実行されている可能性があります。 ランタイムは、どのスレッドがどのアプリケーション ドメインで実行されているかを追跡します。 Thread.GetDomain メソッドを呼び出すことで、スレッドが実行されているドメインをいつでも見つけることができます。
アプリケーション ドメインとカルチャ
CultureInfo オブジェクトによって表されるカルチャは、スレッドに関連付けられます。 CultureInfo.CurrentCulture プロパティを使用して、現在実行中のスレッドに関連付けられているカルチャを取得できます。また、Thread.CurrentCulture プロパティを使用して、現在実行中のスレッドに関連付けられているカルチャを取得または設定できます。 スレッドに関連付けられているカルチャが Thread.CurrentCulture プロパティを使用して明示的に設定されている場合、スレッドがアプリケーション ドメインの境界を越えたときに、そのスレッドに関連付け続けます。 それ以外の場合、特定の時点でスレッドに関連付けられているカルチャは、スレッドが実行されているアプリケーション ドメインの CultureInfo.DefaultThreadCurrentCulture プロパティの値によって決まります。
プロパティの値が
null
されていない場合、プロパティによって返されるカルチャはスレッドに関連付けられます (したがって、 Thread.CurrentCulture プロパティと CultureInfo.CurrentCulture プロパティによって返されます)。プロパティの値が
null
場合、現在のシステム カルチャはスレッドに関連付けられます。
アプリケーション ドメインを使用したプログラミング
アプリケーション ドメインは、通常、ランタイム ホストによってプログラムによって作成および操作されます。 ただし、アプリケーション プログラムでアプリケーション ドメインを操作する必要がある場合もあります。 たとえば、アプリケーション プログラムは、アプリケーション コンポーネントをドメインに読み込んで、アプリケーション全体を停止することなくドメイン (およびコンポーネント) をアンロードできます。
AppDomainは、アプリケーション ドメインへのプログラムによるインターフェイスです。 このクラスには、ドメインの作成とアンロード、ドメイン内の型のインスタンスの作成、アプリケーション ドメインのアンロードなどのさまざまな通知への登録を行うメソッドが含まれています。 次の表に、一般的に使用される AppDomain メソッドの一覧を示します。
AppDomain メソッド | 説明 |
---|---|
CreateDomain | 新しいアプリケーション ドメインを作成します。 AppDomainSetup オブジェクトを指定するこのメソッドのオーバーロードを使用することをお勧めします。 これは、アプリケーション ベースやアプリケーションのルート ディレクトリなど、新しいドメインのプロパティを設定する場合に推奨される方法です。ドメインの構成ファイルの場所。および共通言語ランタイムがアセンブリをドメインに読み込むのに使用する検索パス。 |
ExecuteAssembly と ExecuteAssemblyByName | アプリケーション ドメインでアセンブリを実行します。 これはインスタンス メソッドであるため、参照先の別のアプリケーション ドメインでコードを実行するために使用できます。 |
CreateInstanceAndUnwrap | アプリケーション ドメインに指定した型のインスタンスを作成し、プロキシを返します。 作成された型を含むアセンブリを呼び出し元のアセンブリに読み込むのを回避するには、このメソッドを使用します。 |
Unload | ドメインを正常にシャットダウンします。 アプリケーション ドメインは、ドメインで実行されているすべてのスレッドが停止するか、ドメイン内に存在しなくなったまでアンロードされません。 |
注
共通言語ランタイムはグローバル メソッドのシリアル化をサポートしていないため、デリゲートを使用して他のアプリケーション ドメインでグローバル メソッドを実行することはできません。
共通言語ランタイムのホスティング インターフェイス仕様で説明されているアンマネージ インターフェイスは、アプリケーション ドメインへのアクセスも提供します。 ランタイム ホストは、アンマネージ コードのインターフェイスを使用して、プロセス内のアプリケーション ドメインを作成してアクセスできます。
COMPLUS_LoaderOptimization環境変数
実行可能アプリケーションの既定のローダー最適化ポリシーを設定する環境変数。
構文
COMPLUS_LoaderOptimization = 1
注釈
一般的なアプリケーションでは、含まれているコードを実行する前に、複数のアセンブリをアプリケーション ドメインに読み込みます。
アセンブリの読み込み方法によって、Just-In-Time (JIT) コンパイル済みコードをプロセス内の複数のアプリケーション ドメインで共有できるかどうかが決まります。
アセンブリがドメインに依存しない状態で読み込まれる場合、同じセキュリティ許可セットを共有するすべてのアプリケーション ドメインで、同じ JIT コンパイル コードを共有できます。 これにより、アプリケーションに必要なメモリが減ります。
ドメインに依存しないアセンブリが読み込まれていない場合、アセンブリは読み込まれるすべてのアプリケーション ドメインで JIT コンパイルする必要があり、ローダーはアプリケーション ドメイン間で内部リソースを共有してはなりません。
1 に設定すると、COMPLUS_LoaderOptimization環境フラグは、SingleDomain と呼ばれるドメインに依存しない方法ですべてのアセンブリを読み込むようランタイム ホストに強制します。 SingleDomain は、常にドメインニュートラルで読み込まれる Mscorlib を除き、アセンブリをドメインニュートラルとして読み込まない。 この設定は、ホストがプロセス内で 1 つのアプリケーションのみを実行している場合に一般的に使用されるため、単一ドメインと呼ばれます。
注意事項
COMPLUS_LoaderOptimization環境フラグは、診断とテストのシナリオで使用するように設計されています。 フラグをオンにすると、深刻なスローダウンが発生し、メモリ使用量が増加する可能性があります。
コード例
IISADMIN サービスで、すべてのアセンブリがドメインニュートラルとして読み込まれないように強制するには、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN キーの環境設定のマルチ文字列値に COMPLUS_LoaderOptimization=1
を追加します。
Key = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN
Name = Environment
Type = REG_MULTI_SZ
Value (to append) = COMPLUS_LoaderOptimization=1
こちらも参照ください
.NET