私は.Netを初めて使用し、最初に基本を理解しようとしています。MSILとJavaバイトコードの違いは何ですか?
8 に答える
最初に言っておきますが、Java バイトコードと MSIL の微妙な違いは、初心者の .NET 開発者を悩ませるものではないと思います。どちらも、最終的に使用される物理マシンの上のレイヤーである抽象的なターゲットマシンを定義するという同じ目的を果たします。
MSIL と Java バイトコードは非常によく似ています。実際、 MSIL を Java バイトコードに変換するGrasshopperというツールがあります。私は、Grasshopper の開発チームの一員だったので、私の (薄れている) 知識を少し共有できます。.NET Framework 2.0 が登場した頃に、私はこの作業を中止したことに注意してください。そのため、これらの事柄のいくつかは、もはや真実ではない可能性があります (もしそうなら、コメントを残してください。修正します)。
- .NET では、通常の参照セマンティクスに並置された値セマンティクスを持つユーザー定義型を使用できます(
struct
)。 - .NET は unsigned 型をサポートしているため、命令セットが少し豊富になります。
- Java では、バイトコードにメソッドの例外仕様が含まれています。通常、例外指定はコンパイラによってのみ強制されますが、デフォルト以外のクラスローダーが使用されている場合は、JVM によって強制される場合があります。
- .NET ジェネリックは IL で表現されますが、Java ジェネリックは型消去のみを使用します。
- .NET 属性は、Java には同等のものはありません (これはまだ本当ですか?)。
- .NET
enums
は整数型のラッパー以上のものではありませんが、Javaenums
はほぼ完全に完成したクラスです (コメントを提供してくれたInternet Friendに感謝します)。 - .NET には
out
とref
パラメータがあります。
他にも言語の違いはありますが、それらのほとんどはバイト コード レベルで表現されてstatic
いません。内部クラスのコンストラクターを呼び出し、外部オブジェクトを渡します。同じことが .NET ラムダ式にも当てはまります。
CIL (MSIL の正式名称) と Java バイトコードは、異なるというよりは同じです。ただし、いくつかの重要な違いがあります。
1) CIL は最初から複数の言語のターゲットとして機能するように設計されています。そのため、符号付きおよび符号なしの型、値の型、ポインター、プロパティ、デリゲート、イベント、ジェネリック、単一のルートを持つオブジェクト システムなど、より豊富な型システムをサポートしています。CIL は、グローバル関数や末尾呼び出しの最適化など、初期の CLR 言語 (C# および VB.NET) には必要のない機能をサポートしています。それに比べて、Java バイトコードは Java 言語のターゲットとして設計されており、Java 自体に見られる制約の多くを反映しています。Javaバイトコードを使ってCやSchemeを書くのはもっと難しいでしょう。
2) CIL は、ネイティブ ライブラリとアンマネージ コードに簡単に統合できるように設計されています。
3) Java バイトコードは解釈またはコンパイルされるように設計されましたが、CIL は JIT コンパイルのみを想定して設計されました。つまり、 Monoの初期実装では、JIT の代わりにインタープリターが使用されていました。
4) CIL は、バイトコード形式に直接マッピングされる人間が読み書きできるアセンブリ言語形式を持つように設計 (および指定) されました。私は、Java バイトコードは (名前が示すように) 機械で読み取り可能であることを意図していたと思います。もちろん、Java バイトコードは比較的簡単に元の Java に逆コンパイルでき、以下に示すように「逆アセンブル」することもできます。
JVM (それらのほとんど) は、CLR (それらのいずれか) よりも高度に最適化されていることに注意してください。したがって、生のパフォーマンスが、Java バイトコードをターゲットにすることを好む理由かもしれません。ただし、これは実装の詳細です。
CIL は Windows 専用に設計されているのに対し、Java バイトコードはマルチプラットフォーム用に設計されていると言う人もいます。これはそうではありません。.NET フレームワークにはいくつかの "Windows" 主義がありますが、CIL にはありません。
上記のポイント 4) の例として、私はおもちゃの Java to CIL コンパイラを少し前に書きました。このコンパイラに次の Java プログラムをフィードすると、次のようになります。
class Factorial{
public static void main(String[] a){
System.out.println(new Fac().ComputeFac(10));
}
}
class Fac {
public int ComputeFac(int num){
int num_aux ;
if (num < 1)
num_aux = 1 ;
else
num_aux = num * (this.ComputeFac(num-1)) ;
return num_aux ;
}
}
私のコンパイラは次の CIL を出力します。
.assembly extern mscorlib { }
.assembly 'Factorial' { .ver 0:0:0:0 }
.class private auto ansi beforefieldinit Factorial extends [mscorlib]System.Object
{
.method public static default void main (string[] a) cil managed
{
.entrypoint
.maxstack 16
newobj instance void class Fac::'.ctor'()
ldc.i4 3
callvirt instance int32 class Fac::ComputeFac (int32)
call void class [mscorlib]System.Console::WriteLine(int32)
ret
}
}
.class private Fac extends [mscorlib]System.Object
{
.method public instance default void '.ctor' () cil managed
{
ldarg.0
call instance void object::'.ctor'()
ret
}
.method public int32 ComputeFac(int32 num) cil managed
{
.locals init ( int32 num_aux )
ldarg num
ldc.i4 1
clt
brfalse L1
ldc.i4 1
stloc num_aux
br L2
L1:
ldarg num
ldarg.0
ldarg num
ldc.i4 1
sub
callvirt instance int32 class Fac::ComputeFac (int32)
mul
stloc num_aux
L2:
ldloc num_aux
ret
}
}
ilasm.exe
これは、実行可能ファイルを作成するのと同じように、CIL アセンブラーにフィードできる有効な CIL プログラムです。ご覧のとおり、CIL は完全に人間が読み書きできる言語です。有効な CIL プログラムは、任意のテキスト エディタで簡単に作成できます。
上記の Java プログラムをjavac
コンパイラでコンパイルし、結果のクラス ファイルをjavap
「逆アセンブラ」で実行して、次のようにすることもできます。
class Factorial extends java.lang.Object{
Factorial();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3; //class Fac
6: dup
7: invokespecial #4; //Method Fac."<init>":()V
10: bipush 10
12: invokevirtual #5; //Method Fac.ComputeFac:(I)I
15: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
18: return
}
class Fac extends java.lang.Object{
Fac();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public int ComputeFac(int);
Code:
0: iload_1
1: iconst_1
2: if_icmpge 10
5: iconst_1
6: istore_2
7: goto 20
10: iload_1
11: aload_0
12: iload_1
13: iconst_1
14: isub
15: invokevirtual #2; //Method ComputeFac:(I)I
18: imul
19: istore_2
20: iload_2
21: ireturn
}
出力は(javap
私の知る限り) コンパイルできませんが、上記の CIL 出力と比較すると、2 つが非常に似ていることがわかります。
これらは基本的に同じことを行っています。MSIL は Microsoft のバージョンの Java バイトコードです。
内部の主な違いは次のとおりです。
- バイトコードはコンパイルと解釈の両方のために開発されましたが、MSIL は明示的に JIT コンパイル用に開発されました。
- MSIL は複数の言語 (C# や VB.NET など) をサポートするために開発されましたが、Bytecode は Java 用にのみ記述されているため、バイトコードは特定の .NET 言語に対する IL よりも構文的に Java に似ています。
- MSIL では、値型と参照型がより明確に区別されています
より多くの情報と詳細な比較については、K John Gough によるこの記事(postscript document)を参照してください。
それほど違いはありません。どちらもあなたが書いたコードの中間形式です。実行されると、仮想マシンは管理された中間言語を実行します。つまり、仮想マシンが変数と呼び出しを制御します。今は覚えていませんが、.Net と Java で同じように実行できる言語さえあります。
基本的に、それは同じものの別の形式です
編集: 言語を見つけました (Scala 以外): FAN ( http://www.fandev.org/ ) です。非常に興味深いようですが、まだ評価する時間はありません。
CIL 別名 MSIL は、人間が判読できるように設計されています。Java バイトコードではありません。
Java バイトコードは、存在しない (ただし、JVM がエミュレートする) ハードウェアのマシン コードと考えてください。
CIL はアセンブリ言語に似ており、機械コードから一歩離れていますが、人間が判読できます。
MSIL は Java バイトコードと比較すべきではなく、「Java バイトコードを構成する命令」と比較すべきだと思います。
逆アセンブルされた Java バイトコードの名前はありません。「Java Bytecode」は、公式文書でその名前を見つけることができないため、非公式のエイリアスである必要があります。 Javaクラスファイル逆アセンブラーは言う
クラス内のメソッドごとに、逆アセンブルされたコード (Java バイトコードを構成する命令) を出力します。これらは、Java 仮想マシン仕様に記載されています。
「Java VM 命令」と「MSIL」の両方が、人間が判読できない .NET バイトコードと Java コードにアセンブルされます。
同意すると、違いは初心者として無視するのに十分なほど小さいです。.Net を基礎から学びたい場合は、Common Language Infrastructure と Common Type System を参照することをお勧めします。
Serge Lidin は、MSIL の詳細に関する適切な本を執筆しました: Expert .NET 2.0 IL Assembler。また、 .NET ReflectorとIldasm (Tutorial)を使用した簡単な方法を見て、MSIL をすばやく理解することができました。
MSIL と Java バイトコードの概念は非常に似ています。