MSIL と Java バイトコードの違いと呼ばれる質問への一種のフォローアップとして? 、Java 仮想マシンの動作と。ネットフレームワーク共通言語ランタイム (CLR) は動作しますか?
また、。ネットフレームワークCLRは「仮想マシン」ですか、それとも仮想マシンの属性を持っていませんか?
MSIL と Java バイトコードの違いと呼ばれる質問への一種のフォローアップとして? 、Java 仮想マシンの動作と。ネットフレームワーク共通言語ランタイム (CLR) は動作しますか?
また、。ネットフレームワークCLRは「仮想マシン」ですか、それとも仮想マシンの属性を持っていませんか?
両方の実装には多くの類似点があります (私の意見では、はい、どちらも「仮想マシン」です)。
1 つには、どちらもスタックベースの VM であり、x86 や PowerPC などの最新の CPU で見られるような「レジスタ」の概念がありません。すべての式 ((1 + 1) / 2) の評価は、オペランドを「スタック」にプッシュし、命令 (加算、除算など) がそれらのオペランドを消費する必要があるたびに、それらのオペランドをスタックからポップすることによって実行されます。各命令は、その結果をスタックにプッシュします。
世界中のほぼすべての CPU がスタックを持っているため、仮想マシンを実装するのに便利な方法ですが、レジスタの数はしばしば異なります (また、一部のレジスタは特殊な目的であり、各命令はオペランドが異なるレジスタにあることを期待するなど)。 )。
したがって、抽象的なマシンをモデル化する場合は、純粋にスタックベースのモデルが適しています。
もちろん、実際のマシンはそのようには動作しません。そのため、JIT コンパイラは、バイトコード操作の「登録」を実行し、基本的に実際の CPU レジスタを可能な限りオペランドと結果を含むようにスケジュールします。
これが、CLR と JVM の最大の共通点の 1 つだと思います。
違いについては...
2 つの実装の興味深い違いの 1 つは、CLR には、ジェネリック型を作成し、それらの型にパラメトリック特殊化を適用するための命令が含まれていることです。そのため、実行時に、CLR は List<int> を List<String> とはまったく異なる型と見なします。
内部的には、すべての参照型の特殊化に同じ MSIL を使用します (したがって、List<String> は List<Object> と同じ実装を使用し、API 境界で異なる型キャストを使用します)。独自の実装 (List<int> は List<double> とはまったく異なるコードを生成します)。
Java では、ジェネリック型は純粋にコンパイラのトリックです。JVM には、どのクラスに型引数があるかという概念がなく、実行時にパラメトリックな特殊化を実行できません。
実際的な観点からは、ジェネリック型で Java メソッドをオーバーロードできないことを意味します。List<String> または List<Date> を受け入れるかどうかだけが異なる、同じ名前の 2 つの異なるメソッドを持つことはできません。もちろん、CLR はパラメトリック型を認識しているため、ジェネリック型の特殊化でオーバーロードされたメソッドを問題なく処理できます。
日常的に、これが CLR と JVM の違いであることに最も気付きます。
その他の重要な違いは次のとおりです。
CLR にはクロージャーがあります (C# デリゲートとして実装されます)。JVM は、Java 8 以降でのみクロージャをサポートします。
CLR にはコルーチンがあります (C# の「yield」キーワードで実装されています)。JVM にはありません。
CLR を使用すると、ユーザー コードで新しい値の型 (構造体) を定義できますが、JVM は値の型 (byte、short、int、long、float、double、char、boolean) の固定コレクションを提供し、ユーザーのみが新しい参照を定義できるようにします。タイプ(クラス)。
CLR は、ポインターの宣言と操作をサポートします。JVM と CLR の両方が、厳密な世代圧縮ガベージ コレクターの実装をメモリ管理戦略として採用しているため、これは特に興味深いことです。通常の状況では、厳密な圧縮 GC はポインターを扱うのに非常に苦労します。あるメモリー位置から別のメモリー位置に値を移動すると、すべてのポインター (およびポインターへのポインター) が無効になるためです。ただし、CLR は "固定" メカニズムを提供するため、開発者は、CLR が特定のポインターを移動することを許可されていないコード ブロックを宣言できます。とても便利です。
JVM のコードの最大単位は、「保護された」キーワードによって証明される「パッケージ」か、クラスパスで jar を指定してフォルダーのように扱うことができることによって証明される JAR (Java ARchive) のいずれかです。コードの。CLR では、クラスは「アセンブリ」に集約されます。CLR は、アセンブリ (「AppDomains」に読み込まれ、メモリ割り当てとコード実行のためのサブアプリケーション レベルのサンドボックスを提供する) について推論し、操作するためのロジックを提供します。
CLR バイトコード形式 (MSIL 命令とメタデータで構成される) は、JVM よりも命令の種類が少なくなります。JVM では、すべての固有の操作 (2 つの int 値の加算、2 つの float 値の加算など) には固有の命令があります。CLR では、MSIL 命令はすべてポリモーフィック (2 つの値を加算) であり、JIT コンパイラはオペランドの型を決定し、適切なマシン コードを作成します。ただし、どちらが望ましい戦略であるかはわかりません。どちらにもトレードオフがあります。JVM 用の HotSpot JIT コンパイラは、より単純なコード生成メカニズムを使用できます (オペランドの型は命令で既にエンコードされているため、オペランドの型を決定する必要はありません)。より多くの命令タイプ。
私は約 10 年間 Java を使用しています (そして JVM に憧れています)。
しかし、私の意見では、CLR は現在、ほぼすべての点で優れた実装になっています。
最初の質問は、JVM と .NET Framework を比較することです。実際には、代わりに CLR と比較するつもりだったと思います。もしそうなら、あなたはこれについて小さな本を書くことができると思います (編集: Benji はすでに持っているようです :-)
重要な違いの 1 つは、JVM とは異なり、CLR が言語に依存しないアーキテクチャになるように設計されていることです。
もう 1 つの重要な違いは、CLR がネイティブ コードとの高レベルの相互運用性を可能にするように特別に設計されていることです。つまり、CLR は、ネイティブ メモリにアクセスして変更するときの信頼性とセキュリティを管理し、CLR ベースのデータ構造とネイティブ データ構造の間のマーシャリングも管理する必要があります。
2 番目の質問に答えるために、「仮想マシン」という用語は、ハードウェアの世界 (たとえば、1960 年代の IBM の 360 の仮想化) からの古い用語であり、同じ種類のことを達成するための基盤となるマシンのソフトウェア/ハードウェア エミュレーションを意味していました。 VMWareが行うこと。
CLR は、「実行エンジン」と呼ばれることがよくあります。このコンテキストでは、これは x86 上の IL マシンの実装です。これは JVM が行うことでもありますが、CLR のポリモーフィック バイトコードと JVM の型付きバイトコードの間には重要な違いがあると言えます。
したがって、2 番目の質問に対する衒学的な答えは「いいえ」です。しかし、実際には、これら 2 つの用語をどのように定義するかにかかっています。
編集: JVM と CLR のもう 1 つの違いは、JVM (バージョン 6) は、割り当てられたメモリをオペレーティング システムに解放することを非常に嫌がるということです。
たとえば、JVM プロセスが開始され、最初にオペレーティング システムから 25 MB のメモリが割り当てられたとします。次に、アプリ コードは、追加の 50 MB を必要とする割り当てを試みます。JVM は、オペレーティング システムからさらに 50 MB を割り当てます。アプリケーション コードがそのメモリの使用を停止すると、ガベージ コレクションが行われ、JVM ヒープ サイズが減少します。ただし、JVM は、割り当てられたオペレーティング システム メモリを特定の非常に特殊な状況でのみ解放します。そうしないと、残りのプロセスの存続期間中、そのメモリは割り当てられたままになります。
一方、CLR は、割り当てられたメモリが不要になった場合にオペレーティング システムに解放します。上記の例では、ヒープが減少すると、CLR はメモリを解放します。
CLR と JVM はどちらも仮想マシンです。
.NET Framework と Java ランタイム環境は、それぞれの VM とそのライブラリをバンドルしたものです。ライブラリがなければ、VM はほとんど役に立ちません。
違いの詳細については、さまざまな学術的および個人的な情報源から入手できます。良い例はCLR Design Choicesです。
いくつかの具体的な例は次のとおりです。
これは仮想マシンではありません。.net フレームワークは、最初の実行時にアセンブリをネイティブ バイナリにコンパイルします。
コンピューティングでは、ジャスト イン タイム コンパイル (JIT) (動的変換とも呼ばれます) は、コンピューター プログラムの実行時のパフォーマンスを向上させるための手法です。JIT は、ランタイム環境における以前の 2 つのアイデア (バイトコード コンパイルと動的コンパイル) に基づいています。ネイティブに実行する前に、実行時にコードを変換します。たとえば、バイトコードをネイティブ マシン コードに変換します。インタープリターよりもパフォーマンスが向上するのは、コード ブロックの変換結果をキャッシュすることであり、各行またはオペランドが一致するたびに単純に再評価するのではありません (解釈された言語を参照してください)。また、開発時にコードを静的にコンパイルするよりも有利であると判断された場合にコードを再コンパイルでき、セキュリティ保証を実施できる可能性があるため、利点もあります。
Microsoft の .NET Framework、Java のほとんどの実装、および最近の Actionscript 3 など、いくつかの最新のランタイム環境は、高速コード実行のために JIT コンパイルに依存しています。
ソース: http://en.wikipedia.org/wiki/Just-in-time_compilation
.NET フレームワークを追加すると、Java と同様に仮想マシンが含まれます。