MSILとJavaバイトコードの違いと呼ばれる質問への一種のフォローアップとして?、Java Virtual Machineの動作方法とJava Virtual Machineの動作方法の(主な)相違点または類似点。ネットフレームワーク 共通言語ランタイム(CLR)は機能しますか?
また、 。ネットフレームワーク CLRは「仮想マシン」ですか、それとも仮想マシンの属性を持っていませんか?
MSILとJavaバイトコードの違いと呼ばれる質問への一種のフォローアップとして?、Java Virtual Machineの動作方法とJava Virtual Machineの動作方法の(主な)相違点または類似点。ネットフレームワーク 共通言語ランタイム(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では値の型(バイト、ショート、整数、ロング、フロート、ダブル、文字、ブール)の固定コレクションが提供され、ユーザーは新しい参照のみを定義できます。タイプ(クラス)。
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コンパイラーは、オペランドのタイプを判別し、適切なマシンコードを作成します。どちらが望ましい戦略かはわかりませんが。どちらにもトレードオフがあります。HotSpot JITコンパイラは、JVMの場合、より単純なコード生成メカニズムを使用できます(オペランドタイプはすでに命令にエンコードされているため、オペランドタイプを判別する必要はありません)。ただし、より複雑なバイトコード形式が必要です。より多くの命令タイプ。
私は約10年前からJavaを使用しています(JVMを賞賛しています)。
しかし、私の意見では、CLRは現在、ほぼすべての点で優れた実装です。
最初の質問は、JVMと.NET Frameworkを比較することです。実際には、代わりにCLRと比較するつもりだったと思います。もしそうなら、私はこれについて小さな本を書くことができると思います(編集:ベンジはすでに持っているように見えます:-)
重要な違いの1つは、CLRがJVMとは異なり、言語に依存しないアーキテクチャーとして設計されていることです。
もう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 Design Choicesです。
いくつかの具体的な例は次のとおりです。
これは仮想マシンではなく、.netフレームワークは最初の実行時にアセンブリをネイティブバイナリにコンパイルします。
コンピューティングでは、動的変換とも呼ばれるジャストインタイムコンパイル(JIT)は、コンピュータープログラムのランタイムパフォーマンスを向上させるための手法です。JITは、ランタイム環境における以前の2つのアイデア、バイトコードコンパイルと動的コンパイルに基づいています。たとえば、バイトコードをネイティブマシンコードに変換するなど、ネイティブで実行する前に実行時にコードを変換します。インタープリターに対するパフォーマンスの向上は、コードのブロックを変換した結果をキャッシュすることによるものであり、各行またはオペランドが満たされるたびに再評価するだけではありません(インタープリター型言語を参照)。また、開発時にコードを静的にコンパイルするよりも利点があります。これは、コードが有利であることが判明した場合にコードを再コンパイルし、セキュリティを保証できるためです。
Microsoftの.NET Framework、ほとんどのJavaの実装、および最近のActionscript 3などのいくつかの最新のランタイム環境は、高速コード実行のためにJITコンパイルに依存しています。
出典:http : //en.wikipedia.org/wiki/Just-in-time_compilation
.NETフレームワークを追加すると、Javaのように仮想マシンが含まれます。