MSIL からネイティブ コードへのコンパイル
更新 : 2007 年 11 月
MSIL (Microsoft Intermediate Language) は、実行する前に、共通言語ランタイムに対して、対象コンピュータのアーキテクチャ用ネイティブ コードにコンパイルしておく必要があります。.NET Framework には、この変換を実行する 2 つの方法が用意されています。
.NET Framework の Just-In-Time (JIT) コンパイラ
.NET Framework の ネイティブ イメージ ジェネレータ (Ngen.exe)
Just-In-Time コンパイラによるコンパイル
JIT コンパイルは、アプリケーション実行時に、アセンブリの内容が読み込まれて実行される際に、オン デマンドで MSIL をネイティブ コードに変換します。共通言語ランタイムには、サポートされる CPU アーキテクチャごとに JIT コンパイラが用意されているため、開発者は MSIL アセンブリのセットを作成し、それを JIT でコンパイルして、異なるマシン アーキテクチャを持つさまざまなコンピュータ上で実行できます。ただし、マネージ コードがプラットフォーム固有のネイティブ API またはプラットフォーム固有のクラス ライブラリを呼び出す場合、そのコードは特定のオペレーティング システムでしか実行できません。
JIT コンパイルは、実行時に呼び出されることがないコードがあることを考慮しています。つまり、ポータブル実行可能 (PE) ファイル内にあるすべての MSIL をネイディブ コードに変換するために時間とメモリを費やすのではなく、実行時に必要とされる MSIL を変換し、結果として生成されるネイティブ コードをメモリに保存します。こうすることで、そのプロセスのコンテキスト内の後続の呼び出しでこれを利用できます。型が読み込まれて初期化されるとき、ローダーはスタブを作成し、その型の各メソッドにそれを結び付けます。メソッドが初めて呼び出されるとき、スタブは JIT コンパイラに制御を渡します。JIT コンパイラはそのメソッド用の MSIL をネイティブ コードに変換して、生成されたネイティブ コードを直接指すようスタブを変更します。このため、JIT でコンパイルされたメソッドに対するそれ以降の呼び出しでは、ネイティブ コードが直接実行されます。
NGen.exe を使用したインストール時のコード生成
JIT コンパイラは、アセンブリ内に定義された個々のメソッドが呼び出されるときに MSIL をネイティブ コードに変換するため、どうしても実行時のパフォーマンスに影響を出ます。ほとんどの場合、このパフォーマンスの影響は許容範囲内です。より重要な点として、JIT コンパイラが生成したコードは、コンパイルを起動したプロセスにバインドされます。複数のプロセスの間でこれを共有することはできません。生成されたコードを、1 つのアプリケーションの複数の呼び出しの間で、または 1 つのアセンブリ セットを共有する複数のプロセスの間で共有できるようにするために、共通言語ランタイムは事前コンパイル モードをサポートします。この事前コンパイル モードは ネイティブ イメージ ジェネレータ (Ngen.exe) を使用して、JIT コンパイラと同様に MSIL アセンブリをネイティブ コードに変換します。ただし、Ngen.exe の動作は、以下の 3 つの点で JIT コンパイラの動作と異なります。
MSIL からネイティブ コードへの変換を、アプリケーション実行中ではなく、実行前に行います。
メソッドを 1 つずつではなく、アセンブリ全体を一度にコンパイルします。
生成したコードを、ディスク上のファイルとしてネイティブ イメージ キャッシュに保持します。
コードの検証
コード検証を省略できるように管理者がセキュリティ ポリシーを設定していない限り、MSIL からネイティブ コードへのコンパイル時に、MSIL コードは検証プロセスを通過する必要があります。この検証プロセスでは、コードがタイプ セーフかどうかを確認するために、MSIL とメタデータが調べられます。タイプ セーフなコードとは、アクセス権限を与えられているメモリ位置だけにアクセスするコードを意味します。タイプ セーフは、オブジェクトどうしを分離するため、不注意や悪意による破損からオブジェクトを保護することに役立ちます。また、コードに対するセキュリティ制限が強制適用されることも保証されます。
共通言語ランタイムは、検証可能なタイプ セーフ コードが次の条件を満たすことを前提としています。
型への参照には参照される型との間に完全な互換性がある。
適切に定義された操作だけがオブジェクトに対して呼び出される。
ID が正しい。
検証プロセスでは、MSIL コードが調べられ、適切に定義された型だけを使用してメモリ位置にアクセスしたりメソッドを呼び出したりするかどうかが確認されます。たとえば、オブジェクトのフィールドにアクセスするときにメモリ位置をオーバーランできるようなコードは許可されません。また、不正な MSIL があるとタイプ セーフ規則に違反する可能性があるため、MSIL が正しく生成されているかどうかも調べられます。検証プロセスは適切に定義されたタイプ セーフ コードのセットを許可します。許可されるコードはタイプ セーフなコードだけです。ただし、タイプ セーフなコードの中にも、検証プロセスの制約事項のために検証を通過しないコードがあります。また、言語によっては、デザイン上、検証可能なタイプ セーフ コードが生成されない場合もあります。セキュリティ ポリシーがタイプ セーフなコードを必要とするにもかかわらず、コードが検証を通過しない場合には、そのコードの実行時に例外がスローされます。