58

.net CLR を仮想マシンと呼んだ本を読みましたか? 誰もこれを正当化できますか?一部の開発プラットフォームで仮想マシンの概念が必要な理由は何ですか?

完全にオブジェクト指向で .net と同じくらい強力なネイティブ フレームワーク (仮想マシンを使用しないフレームワーク) を開発することはできないのでしょうか?

CLR を仮想マシンと呼んでいる本は「Professional .Net Framework 2.0」です。

4

7 に答える 7

104

ここには多くの誤解があります。本当に必要な場合は、.Netを仮想マシンと考えることができると思いますが、.NetFrameworkが実際にコードを処理する方法を見てみましょう。典型的なシナリオは次のようになります

  1. .Netプログラムは、C#、VB.Net、F#、またはその他の互換性のある言語で記述します。
  2. そのコードは、エンドユーザーのマシンに配布されるJavaのバイトコードに似た中間言語(IL)にコンパイルされます。
  3. エンドユーザーは、適切なバージョンの.Netがインストールされているコンピューターで初めてプログラムを呼び出します。
  4. コンピューターは、これが「生の」マシンコードではなく.Netアセンブリであると認識し、JITコンパイラーに渡します。
  5. JITコンパイラは、ILを完全にネイティブなマシンコードにコンパイルします
  6. ネイティブコードは、このプログラムの実行中、メモリに保存されます。
  7. 保存されたネイティブコードが呼び出され、ILは重要ではなくなります。

ここにはいくつかの重要なポイントがありますが、大きなポイントは、コードが解釈されることは決してないということです。代わりに、ステップ5で、ネイティブコードにコンパイルされていることがわかります。これは、いくつかの理由から、仮想マシンにロードする場合とは大きく異なります。

  1. 完全にコンパイルされたコードは、追加のソフトウェア抽象化レイヤーによって解釈または変換されるのではなく、CPUによって直接実行されます。
  2. JITコンパイラーは、最小公分母を決定するのではなく、プログラムを実行している個々のマシンに固有の最適化を利用できます。
  3. 必要に応じて、コードをプリコンパイルして、本質的にステップ5をユーザーから完全に隠すこともできます。

JITterが開発者から実際のマシンの詳細を抽象化するという意味で、これを仮想マシンと呼ぶことができると思います。個人的には、それが本当に正しいとは思いません。多くの人にとって、仮想マシンは、.Netプログラムには存在しないネイティブコードからのランタイム抽象化を意味するからです。

このプロセス全体について、「仮想マシン」環境とは一線を画すもう1つの重要なポイントは、それが典型的なものにすぎないということです。処理する。本当に必要な場合は、配布前に.Netアセンブリをプリコンパイルし、ネイティブコードをエンドユーザーに直接デプロイできます(ヒント:マシン固有の最適化が失われるため、プログラムの存続期間中、全体として速度が低下します)。もちろん、.Netランタイムをインストールする必要がありますが、現時点では、他のランタイムAPIとそれほど違いはありません。これは、MicrosoftがVisual Studioに同梱しているVBまたはCランタイムの場合と同様に、リンクできる優れたAPIを備えたコレクションdllのようなものです。この種の場合、ILは全体像から外れ、VMモニカを正当化するのがはるかに困難になります。(ILはまだ展開され、保存されたコードを検証するために使用されるため、「種類」と言いますが、実行のためにそれ自体が変更されることはありません)。

もう1つの重要な点は、VMプロセスがないことです。アプリを実行する場合、実行される一般的な「サンドボックス」プロセスはありません。これをJavaと比較してください。プログラムの実行中にタスクマネージャーを開くと、Java VM専用のプロセスが表示され、アプリケーションの実際のプロセスは、VMによって作成されたサンドボックス内のスレッドになります。.Netでは、アプリケーションのプロセスがWindowsタスクマネージャーに直接表示されます。

要約すると、IL + CLR+JITが一緒になって仮想マシンを構成していると言えます。個人的にはそうは思いませんが、あなたがそれを信じるなら、私はあなたと議論しません。私が言いたいのは、.Netが仮想マシンで実行されていることを誰かに伝えると、その人に伝えているのは「ホストプロセスで解釈されるバイトコード」であるということです。そして、それは間違っています。


更新この回答は少し古くなり、.Netを仮想マシンのようにさらに少なくするように状況が変更されました。コンテナーの時代では、コールドスタート時間はさらに長くなる可能性があります。私の理解では、最近のバージョンの.Net Coreには、ネイティブコードのデプロイを可能にし、各スタートアップでJITステップをスキップするためのツールがさらに簡単になっています。

于 2009-10-26T14:19:01.660 に答える
3

JVMもCLRも、他の言語のほとんどの「仮想マシン」が行うことと実質的に異なることは何もしません。最近では、それらはすべて JIT を使用して、仮想命令 (p コード、バイトコード、中間言語命令、好きなように呼びます) を「ネイティブ CPU ハードウェア」命令 (「マシン コード」) に変換します。

実際、これを行う最初の「仮想マシン」は Smalltalk 仮想マシンでした。このイノベーションの作者である Peter Deutsch は、Java によって一般化された「JIT」という用語の代わりに、「動的翻訳」と呼んだ。Smalltalk の「ランタイム実行環境」が「仮想マシン」と呼ばれる場合 (それは今でもそう呼ばれています)、本質的に同じことを行う他のすべての「ランタイム システム」も「仮想マシン」と見なされます。 "

于 2014-07-03T22:28:54.063 に答える
1

貴重な答えはたくさんありますが、まだ言及されていないことが1つあります。それはモジュール性です。

ネイティブDLLからOOクラスをエクスポートするのは非常に困難です。もちろん、リンカにクラスをエクスポートして別の場所にインポートするように指示することはできますが、これは脆弱です。クラス内の単一のプライベートメンバーを変更すると、バイナリ互換性が失われます。つまり、他のすべてのモジュールを再コンパイルせずに1つのDLLを変更すると、実行時にプログラムがひどくクラッシュします。

これを回避する方法はいくつかあります。たとえば、パブリック抽象インターフェイスを定義し、それらから派生して、DLLからグローバルファクトリ関数をエクスポートできます。このようにして、クラスの実装の詳細を変更できます。ただし、別のDLLでそのクラスから派生することはできません。もちろん、インターフェイスを変更すると、バイナリ互換性も失われます。

ネイティブコードでこれに対する適切な解決策があるかどうかはわかりません。コンパイラ/リンカーがコンパイル時にネイティブコードを作成する場合、コードで使用されるクラス/構造の正確なメモリレイアウトを知っている必要があります。メソッドが最初に呼び出されるまで最後のコンパイルステップ(ネイティブコードの生成)が遅れると、この問題は簡単に解消されます。アセンブリ内のクラスを変更でき、JITが使用されているすべてのメンバーを解決できる限りランタイム、すべてが正常に実行されます。

一言で言えば:モノリシックな単一実行可能プログラムを作成する場合、ネイティブコードを作成するコンパイラを使用して.NETの強力な機能のほとんどを使用できる可能性があります。ただし、JITコンパイラを使用することのデメリット(フレームワークのインストール、起動時間がわずかに長くなる)は、ほとんどの場合、メリットを実際に上回りません。

于 2009-10-26T15:06:38.117 に答える