1. ホーム
  2. java

[解決済み] Javaの仮想マシンとCLR

2022-07-03 23:28:16

質問

という質問のフォローアップのようなものです。 MSILとJavaバイトコードの違い? という質問のフォローとして、Java仮想マシンの動作とMSILの動作の(主要な)違いや類似点は何ですか? .NET フレームワーク 共通言語ランタイム (CLR) がどのように動作するか?

また .NET フレームワーク CLR は "仮想マシン" なのでしょうか、それとも仮想マシンの属性を持たないのでしょうか?

どのように解決するのですか?

両方の実装には多くの類似点があります (そして私の意見では、そうです、どちらも "仮想マシン" です)。

たとえば、これらは両方ともスタック ベースの仮想マシンであり、x86 や PowerPC のような最新の CPU で見慣れた「レジスタ」の概念はありません。すべての式((1 + 1) / 2)の評価は、オペランドをスタックにプッシュし、命令(加算、除算など)がオペランドを消費する必要があるときにオペランドをスタックからポップすることによって実行されます。各命令は、その結果をスタックにプッシュし直します。

世界中のほとんどすべての CPU がスタックを持っていますが、レジスタの数はしばしば異なるため、これは仮想マシンを実装するのに便利な方法です (一部のレジスタは特殊で、各命令は異なるレジスタにオペランドを期待する、など)。

ですから、抽象的なマシンをモデル化するのであれば、純粋にスタックベースのモデルはかなり良い方法だと言えます。

もちろん、実際のマシンはそのように動作しません。そのため、JIT コンパイラーはバイトコード操作の "enregistration" を実行する責任があり、基本的に可能な限りオペランドと結果を含むように実際の CPU レジスタをスケジューリングします。

つまり、それがCLRとJVMの間の最大の共通点の1つだと思うのです。

違いとしては...

2 つの実装の興味深い違いの 1 つは、CLR には汎用型を作成するための命令が含まれており、次にそれらの型にパラメトリックな特殊化を適用するための命令が含まれていることです。そのため、実行時に、CLR は List<int> を List<String> と完全に異なる型と見なします。

しかし、それぞれの値型は独自の実装を使用します (List<int> は List<double> と完全に異なるコードを生成します)。

Java では、ジェネリック型は純粋にコンパイラーのトリックです。JVMはどのクラスが型引数を持つかという概念を持たず、実行時にパラメトリックな特殊化を実行することができないのです。

実用的な観点からは、それは、ジェネリックタイプでJavaメソッドをオーバーロードできないことを意味します。同じ名前で、List<String> または List<Date> を受け入れるかどうかだけが異なる、2 つの異なるメソッドを持つことはできません。もちろん、CLRはパラメトリック型について知っているので、ジェネリック型の特殊化でオーバーロードされたメソッドを処理することに問題はありません。

日常的に、これが私が CLR と JVM の間で最も気がつく違いです。 JVM との間の最も顕著な違いです。

その他の重要な違いは以下の通りです。

  • CLRにはクロージャがある(C#のデリゲートとして実装されている)。JVMはJava 8以降のみクロージャをサポートしています。

  • CLRはコルーチン(C#の「yield」キーワードで実装)を持っています。JVMはそうではありません。

  • CLR はユーザー コードが新しい値型 (構造体) を定義することを可能にしますが、JVM は値型 (byte, short, int, long, float, double, char, boolean) の固定コレクションを提供し、ユーザーが新しい参照型 (クラス) を定義することのみを可能にします。

  • CLR はポインターの宣言と操作のサポートを提供します。これは、JVM と CLR の両方がメモリ管理戦略として厳密な世代別コンパクト化ガベージコレクタ実装を採用するため、特に興味深いものです。なぜなら、あるメモリ位置から別のメモリ位置に値を移動すると、すべてのポインタ(およびポインタへのポインタ)が無効になってしまうからです。しかし、CLRにはピン留めというメカニズムがあり、開発者はCLRが特定のポインタを移動してはいけないコードのブロックを宣言することができます。これはとても便利です。

  • JVM におけるコードの最大の単位は、'protected' キーワードによって証明されるように 'package' であるか、またはクラスパスで jar を指定でき、それがコードのフォルダーのように扱われることによって証明されるように、おそらく JAR (i.e. Java ARchive) であるかのいずれかです。CLR では、クラスは「アセンブリ」に集約され、CLR はアセンブリ (これは "AppDomains" にロードされ、メモリ割り当てとコード実行にサブアプリケーション レベルのサンドボックスを提供します) について推論し操作するためのロジックを提供します。

  • CLR バイトコード形式 (MSIL 命令とメタデータで構成される) は、JVM よりも少ない命令タイプを持っています。JVMでは、すべてのユニークな操作(2つのint値の加算、2つのfloat値の加算など)は、それ自身のユニークな命令を持っています。CLRでは、MSIL命令はすべてポリモーフィック(2つの値を足す)であり、JITコンパイラーはオペランドの型を決定し、適切な機械語を作成する責任を負う。どちらが望ましい戦略なのか、私にはわかりません。どちらにもトレードオフがあります。JVM 用の HotSpot JIT コンパイラーは、より単純なコード生成メカニズムを使用できますが (オペランドのタイプを決定する必要はありません。なぜなら、オペランドはすでに命令にエンコードされているからです)、それはより多くの命令タイプで、より複雑なバイトコード形式が必要であることを意味します。

私は約 10 年間 Java を使用してきました (そして JVM に感心していました)。

しかし、私の意見では、CLRはほとんどすべての点で優れた実装であると思います。