JIT コンパイラと CLR の違いは何ですか? コードを il にコンパイルし、CLR がそのコードを実行する場合、JIT は何をしているのでしょうか? CLR にジェネリックが追加されたことで、JIT コンパイルはどのように変化しましたか?
7 に答える
コードを IL にコンパイルします。IL は実行時に実行され、マシン コードにコンパイルされます。これは JIT と呼ばれるものです。
Edit、答えをもう少し肉付けするために(まだ過度に単純化されています):
Visual Studio で C# コードをコンパイルすると、CLR が理解できる IL に変換されます。IL は、CLR 上で実行されているすべての言語で同じです (これにより、.NET ランタイムが複数の言語と相互運用を使用できるようになります)。それらの間で簡単に)。
実行時に、IL はマシン コード (使用しているアーキテクチャに固有) に解釈され、実行されます。このプロセスは、ジャスト イン タイム コンパイルまたは略して JIT と呼ばれます。必要な IL のみがマシン コードに変換され (一度だけ、マシン コードにコンパイルされた後、"キャッシュ" されます)、実行される直前に実行されるため、JIT という名前が付けられています。
C# の場合は次のようになります。
C# コード
>
C# コンパイラ>
IL>
.NET ランタイム>
JIT コンパイラ>
マシンコードの>
実行
そして、これはVBの場合のようになります
VB コード
>
VB コンパイラ>
IL>
.NET ランタイム>
JIT コンパイラ>
マシンコードの>
実行
ご覧のように、最初の 2 つのステップのみが各言語に固有であり、IL に変換された後のすべては同じです。これが、前に述べたように、.NET 上で複数の異なる言語を実行できる理由です。
JITはCLRの1つの側面です。
具体的には、元の言語のコンパイラ(たとえば、Microsoft c#の場合はcsc.exe)によって生成されたCIL(以降、ILと呼びます)を現在のプロセッサ(および現在のプロセスで公開するアーキテクチャ)にネイティブなマシンコードに変更する役割を果たします。例32/64ビット)。問題のアセンブリが生成された場合、JITプロセスは完全に不要であり、CLRはこのコードがなくても問題なく実行されます。
中間表現からまだ変換されていないメソッドを使用する前に、それを変換するのはJITの責任です。JITが開始される
正確な時期は実装固有であり、変更される可能性があります。ただし、CLR設計では、関連するコードが実行される前にJITが発生することが義務付けられていますが、対照的に、JVMはしばらくの間コードを自由に解釈でき、別のスレッドがマシンコード表現を作成します。
「通常の」CLRは、JIT前のスタブアプローチを使用しますここで、byメソッドは、使用されたときにのみJITコンパイルされます。これには、最初のネイティブメソッドスタブを間接参照にして、JITにメソッドをコンパイルし、元の呼び出しを変更して最初のスタブをスキップするように指示することが含まれます。現在のCompactEditionは、代わりに、ロード時に型のすべてのメソッドをコンパイルします。
ジェネリックの追加に対処するため。
これは、内部実装の詳細ではなく、セマンティクスの観点から、IL仕様とJITに対する最後の大きな変更でした。
いくつかの新しいIL命令が追加され、インストルメンテーションタイプとメンバーに対してより多くのメタデータオプションが提供されました。ILレベルでも制約が追加されました。
JITは、ジェネリック引数を持つメソッドを(包含クラスを介して明示的または暗黙的に)コンパイルするときに、使用されるタイプごとに異なるコードパス(マシンコード命令)を設定する場合があります。実際には、JITはすべての参照型に共有実装を使用します。これは、これらの変数が同じセマンティクスを示し、同じスペース(IntPtr.Size)を占めるためです。
各値型は、そのために生成された特定のコードを取得します。スタック/ヒープ上の変数のサイズの縮小/拡大に対処することが、これの主な理由です。また、メソッドが呼び出される前に制約付きオペコードを発行することにより、非参照型での多くの呼び出しは、メソッドを呼び出すために値をボックス化する必要はありません(この最適化は非ジェネリックの場合にも使用されます)。これにより、デフォルトの<T>
動作を正しく処理し、Null許容値以外の型が使用された場合にnullとの比較を操作なし(常にfalse)として削除することもできます。
実行時にリフレクションを介してジェネリック型のインスタンスを作成しようとすると、型パラメーターが実行時に検証され、制約を通過することが確認されます。これが型システム内で使用されない限り、これはJITに直接影響しません(可能ではありませんが)。
Jon Skeet が言うように、JIT は CLR の一部です。基本的に、これはボンネットの下で起こっていることです:
- ソース コードは、共通中間言語 (CIL) として知られるバイト コードにコンパイルされます。
- すべてのクラスとすべてのメソッド (および他のすべてのもの:O) からのメタデータは、結果の実行可能ファイル (dll または exe) の PE ヘッダーに含まれます。
- 実行可能ファイルを生成する場合、PE ヘッダーには、実行可能ファイルの実行時に CLR (共通言語ランタイム) のロードを担当する従来のブートストラップも含まれます。
今、実行すると:
- ブートストラップは、(主に mscorlib アセンブリを読み込むことによって) CLR を初期化し、アセンブリを実行するように指示します。
- CLR はメイン エントリを実行します。
- 現在、クラスにはメソッド関数のアドレスを保持するベクター テーブルがあるため、MyMethod を呼び出すと、このテーブルが検索され、対応するアドレスへの呼び出しが行われます。開始時に、すべてのテーブルのすべてのエントリが JIT コンパイラのアドレスを持ちます。
- そのようなメソッドの 1 つが呼び出されると、実際のメソッドの代わりに JIT が呼び出され、制御を取得します。次に、JIT は CIL コードを適切なアーキテクチャ用の実際のアセンブリ コードにコンパイルします。
- コードがコンパイルされると、JIT はメソッド ベクター テーブルに入り、アドレスをコンパイル済みコードのアドレスに置き換えます。これにより、後続のすべての呼び出しで JIT が呼び出されなくなります。
- 最後に、JIT はコンパイル済みコードの実行を処理します。
- まだコンパイルされていない別のメソッドを呼び出す場合は、4 に戻ります... など...
JIT は基本的に CLR の一部です。ガベージコレクターは別です。相互運用の責任などをどこに置くかは別の問題であり、私がコメントする資格が非常に不足しているものです:)
スレッドがかなり古いことは知っていますが、JITを理解するための写真を入れるかもしれないと思いました。これは、 Jeffrey RitcherによるC#経由の優れた本CLRからのものです。写真では、彼が話しているメタデータは、アセンブリヘッダーで出力されたメタデータであり、アセンブリ内のタイプに関するすべての情報が格納されています。
1) .net プログラムのコンパイル中に、.net プログラム コードは中間言語 (IL) コードに変換されます。
2) プログラムを実行すると、中間言語コードは、メソッドが呼び出されたときにオペレーティング システムのネイティブ コードに変換されます。これは JIT (ジャスト イン タイム) コンパイルと呼ばれます。
- 共通言語ランタイム (CLR) はインタープリターであり、Just In Time (JIT) は .Net Framework のコンパイラーです。
2.JIT は .NET の内部コンパイラであり、CLR から MicroSoft Intermediate Code Language (MSICL) コードを取得し、それをマシン固有の命令に実行しますが、CLR はエンジンとして機能します。その主なタスクは、MSICL コードを JIT に提供して、コードが確実に実行されるようにすることです。マシンの仕様に従って完全にコンパイルされています。