30

この質問を読んで、Java仮想マシンと.NET CLRの違いを調べました。ベンジの答えから、そもそもなぜ仮想マシンが必要なのか疑問に思いました。

Benjiの説明を理解したところ、仮想マシンのJITコンパイラは、中間コードをCPU上で実行される実際のアセンブリコードに解釈します。これを行う必要がある理由は、CPUのレジスタ数が異なることが多く、Benjiによれば、「一部のレジスタは特殊用途であり、各命令は異なるレジスタのオペランドを期待している」ためです。これは、同じコードを任意のCPUで実行できるようにするために、仮想マシンのような中間インタープリターが必要であることを意味します。

しかし、その場合、私が理解していないのは、マシンコードにコンパイルされたCまたはC ++コードが、正しいOSである限り、どのコンピューターでも実行できる理由です。では、なぜPentiumを使用してWindowsマシンでコンパイルしたCプログラムが、AMDを使用して他のWindowsマシンで実行できるのでしょうか。

Cコードを任意のCPUで実行できる場合、仮想マシンの目的は何ですか?同じコードをどのOSでも実行できるようにするためですか?JavaにはほとんどすべてのOSでVMバージョンがあることは知っていますが、Windows以外の他のOS用のCLRはありますか?

それとも私が見逃しているものが他にありますか?OSは、特定のCPUなどに適合させるために、実行するアセンブリコードの他の解釈を行いますか?

これらすべてがどのように機能するのか非常に興味がありますので、明確な説明をいただければ幸いです。

注:JVMとCLRの質問でクエリをコメントとして投稿しなかった理由は、コメントを投稿するのに十分なポイントがまだないためです=b。

編集:すべての素晴らしい答えをありがとう!したがって、私が見逃していたのは、すべてのプロセッサに違いはありますが、共通の標準化、主にX86アーキテクチャがあり、1つのX86プロセッサでコンパイルされたCコードがほとんどの部分で機能するように十分な数の共通機能を提供することでした。別のX86プロセッサで。これにより、ガベージコレクションの重要性を忘れたことは言うまでもなく、仮想マシンの正当性がさらに高まります。

4

10 に答える 10

42

AMD プロセッサと Intel プロセッサは、同じ命令セットとマシン アーキテクチャを使用します (マシン コードの実行の観点から)。

C および C++ コンパイラは、対象の OS に適したヘッダーを使用してマシン コードにコンパイルします。コンパイルされると、それらはコンパイルされた言語と何らかの形で関連付けられなくなり、単なるバイナリ実行可能ファイルになります。(どの言語からコンパイルされたかを示すアーティファクトがありますが、それはここでは重要ではありません)

したがって、コンパイルされると、それらはマシン (X86、Intel および AMD の命令セットとアーキテクチャ) および OS に関連付けられます。

これが、互換性のある x86 マシンおよび互換性のある OS (一部のソフトウェアでは win95 から winvista) で実行できる理由です。

ただし、Intel プロセッサで実行されている場合でも、OSX マシンでは実行できません。バイナリは、追加のエミュレーション ソフトウェア (パラレルや Windows を備えた VM など) を実行しない限り、互換性がありません。

さらに、ARM プロセッサ、MIPS、または PowerPC でそれらを実行する場合は、X86 のバイナリ マシン コードを実行しているマシンに解釈する完全なマシン命令セット エミュレータを実行する必要があります。

それを .NET と比較してください。

.NET 仮想マシンは、オブジェクト、メモリ割り当て、ガベージ コレクション、およびその他の高レベルの構造を理解するプロセッサなど、世界中にはるかに優れたプロセッサがあるかのように製造されています。これは非常に複雑なマシンであり、現在 (良好なパフォーマンスで) シリコンに直接構築することはできませんが、既存のプロセッサで実行できるエミュレータを作成することはできます。

突然、.NET を実行したい任意のプロセッサ用に 1 つのマシン固有のエミュレータを作成でき、その上で任意の .NET プログラムを実行できます。OS や基盤となる CPU アーキテクチャについて心配する必要はありません。.NET VM があれば、ソフトウェアは実行されます。

しかし、もう少し先に進みましょう。この共通言語ができたら、他の書き言葉をそれに変換するコンパイラを作成してみませんか?

これで、C、C#、C++、Java、javascript、Basic、python、lua、または記述されたコードを変換してこの仮想マシンで実行できるその他の言語コンパイラを使用できるようになりました。

マシンと言語の関連付けを 2 度分離しました。2 段階の分離をマッピングするコンパイラと VM が存在する限り、それほど多くの作業を行うことなく、誰でも任意のコードを作成し、任意のマシンで実行できます。 .

なぜこれが良いことなのかまだ疑問に思っている場合は、初期の DOS マシンと、世界に対する Microsoft の本当の貢献は何であったかを考えてみてください。

Autocad は、印刷できるプリンタごとにドライバを作成する必要がありました。ロータス 1-2-3 も同様です。実際、ソフトウェアで印刷するには、独自のドライバーを作成する必要がありました。10 台のプリンターと 10 個のプログラムがある場合、本質的に同じコードの 100 個の異なる部分を別々に独立して作成する必要がありました。

Windows 3.1 が (GEM や他の多くの抽象化レイヤーと共に) 達成しようとしたことは、プリンターの製造元がプリンター用に 1 つのドライバーを作成し、プログラマーが Windows プリンター クラス用に 1 つのドライバーを作成するようにすることでした。

10 個のプログラムと 10 個のプリンターがあれば、20 個のコードを書くだけで済みます。Microsoft 側のコードは誰にとっても同じだったので、MS の例は、実行する作業がほとんどないことを意味します。

現在、プログラムは、サポートすることを選択した 10 台のプリンターだけに制限されているのではなく、製造元が Windows 用のドライバーを提供しているすべてのプリンターに制限されていました。

アプリケーション開発でも同様の問題が発生しています。MAC を使用していないため、使用できない非常に優れたアプリケーションがあります。大量の重複があります (世界クラスのワード プロセッサが実際に何台必要か?)。

Java はこれを修正することを意図していましたが、多くの制限があり、そのうちのいくつかは実際には解決されていません。

.NET の方が近いですが、Windows 以外のプラットフォーム向けにワールドクラスの VM を開発している人は誰もいません (mono は非常に近いですが、まだ十分ではありません)。

だから...だから、VMが必要なのです。私とは異なる OS とマシンの組み合わせを選んだという理由だけで、少数の聴衆に限定したくないからです。

-アダム

于 2009-01-31T02:08:38.217 に答える
8

C コードが任意のプロセッサで実行できるというあなたの仮定は正しくありません。レジスターやエンディアンなどによって、コンパイルされた C プログラムが、あるプラットフォームではまったく動作しなくても、別のプラットフォームでは動作する場合があります。

ただし、プロセッサが共有する特定の類似点があります。たとえば、Intel x86 プロセッサと AMD プロセッサは、一方に対してコンパイルされたほとんどのコードが他方で実行される十分に大きなプロパティ セットを共有します。ただし、プロセッサ固有のプロパティを使用する場合は、それを行うコンパイラまたは一連のライブラリが必要です。

仮想マシンが必要な理由については、プロセッサの違いを処理するという声明を超えて、現在 C++ でコンパイルされた (管理されていない) プログラムでは利用できないコードに仮想マシンがサービスを提供するという事実もあります。

提供される最も顕著なサービスは、CLR と JVM によって提供されるガベージ コレクションです。これらの仮想マシンは両方とも、このサービスを無料で提供します。彼らはあなたのためにメモリを管理します。

境界チェック、アクセス違反 (まだ可能ですが、非常に困難です) なども提供されます。

CLR は、コード セキュリティの形式も提供します。

これらのいずれも、仮想マシンで動作しない他の多くの言語の基本的なランタイム環境の一部として提供されていません。

ライブラリを使用してそれらの一部を取得することもできますが、その場合、ライブラリを使用するパターンが強制されますが、.NET および Java では、CLR および JVM を通じて提供されるサービスは一貫してアクセスされます。

于 2009-01-31T01:56:34.917 に答える
5

基本的に、「マネージ コード」が可能になります。これはまさにその言葉を意味します。つまり、仮想マシンは実行時にコードを管理します。この 3 つの主な利点は、ジャストインタイム コンパイル、マネージ ポインター/ガベージ コレクション、およびセキュリティ制御です。

ジャストインタイム コンパイルの場合、仮想マシンはコードの実行を監視し、コードがより頻繁に実行されると、より高速に実行されるように再最適化されます。ネイティブ コードではこれを行うことはできません。

マネージド ポインターは最適化も容易です。これは、仮想マシンがポインターの移動を追跡し、サイズと有効期間に応じてさまざまな方法で管理するためです。C++ でこれを行うのは困難です。これは、コードを読むだけではポインターがどこに行くのかを実際に判断できないためです。

セキュリティは一目瞭然です。仮想マシンは、監視しているため、コードがすべきでないことを実行するのを防ぎます。個人的には、これが Microsoft が C# にマネージ コードを選択した最大の理由だと思います。

基本的に私の言いたいことは、仮想マシンはコードが発生するのを監視できるので、プログラマーの作業を楽にし、コードを高速化することを実行できるということです。

于 2009-01-31T02:05:08.957 に答える
5

ほとんどのコンパイラは、ネイティブ コード コンパイラであっても、ある種の中間言語を使用します。

これは主に、コンパイラの構築コストを削減するために行われます。世界にはたくさんの (N) 個のプログラミング言語があります。世界には多くの (M) ハードウェア プラットフォームもあります。コンパイラが中間言語を使用せずに機能する場合、すべてのハードウェア プラットフォームですべての言語をサポートするために作成する必要がある "コンパイラ" の総数は N*M になります。

ただし、中間言語を定義し、コンパイラをフロント エンドとバック エンドの 2 つの部分に分割し、フロント エンドでソース コードを IL にコンパイルし、バック エンドで IL をマシン コードにコンパイルすることで、書くだけで済みます。 N+M コンパイラ。これは結果的に大幅なコスト削減になります。

CLR / JVM コンパイラとネイティブ コード コンパイラの大きな違いは、フロント エンドとバック エンドのコンパイラが相互にリンクされる方法です。ネイティブ コード コンパイラでは、通常、2 つのコンポーネントが同じ実行可能ファイルに結合され、プログラマが IDE で「ビルド」をクリックすると両方が実行されます。

CLR / JVM コンパイラでは、フロントエンドとバックエンドが異なるタイミングで実行されます。フロントエンドはコンパイル時に実行され、実際に顧客に出荷される IL を生成します。バックエンドは、実行時に呼び出される別のコンポーネントに組み込まれます。

したがって、これは別の質問、「実行時までバックエンドのコンパイルを遅らせることの利点は何ですか?」という別の質問を提起します。

答えは「場合による」です。

バックエンドのコンパイルを実行時まで遅らせることで、複数のハードウェア プラットフォームで実行できる 1 セットのバイナリを出荷することが可能になります。また、プログラムを再デプロイすることなく、バックエンド コンパイル テクノロジの改善を利用できるようになります。また、多くの動的言語機能を効率的に実装するための基盤も提供できます。最後に、事前のマシン コード コンパイルでは不可能な、個別にコンパイルされた動的にリンクされたライブラリ (dll) 間にセキュリティと信頼性の制約を導入する機能を提供します。

ただし、欠点もあります。広範なコンパイラ最適化を実装するために必要な分析は、コストがかかる可能性があります。これは、「JIT」バックエンドが行う最適化が、アップフロント バックエンドよりも少ないことが多いことを意味します。これにより、パフォーマンスが低下する可能性があります。また、実行時にコンパイラを呼び出す必要があるため、プログラムのロードに必要な時間も長くなります。「upfront」コンパイラで生成されたプログラムには、これらの問題はありません。

于 2009-02-02T02:49:47.727 に答える
3

まず、マシンコードは CPU の命令の最低形式ではありません。今日の x86 CPU 自体は、マイクロコードを使用して X86 命令セットを別の内部形式に解釈します。マイクロコードを実際にプログラムするのは、チップ開発エンジニア タイプだけであり、今日のテクノロジを使用して最大のパフォーマンスを達成するために、レガシー x86 命令チップを忠実かつ苦労なくエミュレートします。

開発者タイプは、それらがもたらす力と機能のために、常に抽象化のレイヤーを追加してきました。結局のところ、より優れた抽象化により、新しいアプリケーションをより迅速かつ確実に作成できます。企業は、コードの内容や方法を気にせず、仕事を確実かつ迅速に完了させたいだけです。アプリケーションの C バージョンの所要時間が数ミリ秒短くても、開発にかかる時間が 2 倍になることは本当に問題なのでしょうか?

何百万人もの人々にサービスを提供する多くのエンタープライズ アプリケーションは Java などのプラットフォーム/言語 (GMail、GMaps など) で記述されているため、速度の問題はほとんど議論の余地がありません。どの言語/プラットフォームが最速かは忘れてください。さらに重要なことは、正しいアルゴリズムを使用し、効率的なコードを記述して仕事を成し遂げることです。

于 2009-01-31T02:35:57.973 に答える
2

AMDとIntelのプロセッサはどちらもx86アーキテクチャを備えています。異なるアーキテクチャでc/c ++プログラムを実行する場合は、そのアーキテクチャにコンパイラを使用する必要があります。同じバイナリ実行可能ファイルが異なるプロセッサアーキテクチャ間で実行されることはありません。

于 2009-01-31T01:49:14.590 に答える
2

Java にはほぼすべての OS で VM バージョンがあることは知っていますが、Windows 以外の OS 用の CLR はありますか?

単核症

于 2009-01-31T01:55:06.480 に答える
2

非常に単純化した方法では、Intel と AMD が同じアセンブリ言語を実装し、同じ数のレジスタなどを使用しているためです。

したがって、C コンパイラは Linux で動作するようにコードをコンパイルします。そのアセンブリは Linux ABIを使用しているため、コンパイル プログラムが Linux、x86 アセンブリ、および適切な関数シグネチャで実行されている限り、すべて問題ありません。

次に、そのコンパイルされたコードを取得して、Linux/PPC (古い iBook 上の Linux など) に貼り付けてみてください。それはうまくいきません。JVM は Linux/PPC プラットフォームに実装されているため、Java プログラムとは異なります。

今日のアセンブリ言語は、基本的に、プログラマーがプログラムできるもう 1 つのインターフェイスです。x86 (32 ビット) では、汎用整数レジスタの場合は eax、ebx、ecx、edx、浮動小数点の場合は f00-f07 にアクセスできます。舞台裏では、CPU には実際にはさらに 100 のレジスタがあり、それらをごちゃまぜにしてパフォーマンスを絞り出しています。

于 2009-01-31T01:56:13.607 に答える
0

あなたの分析では正しいです.JavaまたはC#は、任意のマシンで実行するために直接コンパイルするように設計されている可能性があり、それを行うとおそらく高速になります. しかし、仮想マシンのアプローチでは、コードが実行される環境を完全に制御できます。VM は、パスワードの変更や HD ブートセクターの更新など、有害な可能性のあるコードを実行するための適切なセキュリティ アクセスを持つコマンドのみを許可する安全なサンドボックスを作成します。他にも多くの利点がありますが、それが決定的な理由です。C# で StackOverflow を取得できません ...

于 2009-01-31T01:54:43.157 に答える
0

あなたの質問の前提は有効だと思います-あなたは確かにこの質問をする最初の人ではありません. http://llvm.orgをチェックして、別のアプローチを確認してください (現在実行中のプロジェクトであるか、Apple が後援しているプロジェクトでしょうか)。

于 2009-01-31T03:43:04.140 に答える