我是 .Net 的新手,希望先了解一些基础知识。 MSIL 和 Java 字节码有何区别?
MSIL和Java字节码非常相似,事实上,曾经有一个名为Grasshopper的工具,可以将MSIL转换为Java字节码。我曾经是Grasshopper开发团队的一员,因此我可以分享一些我(已经褪色的)知识。请注意,我停止在.NET Framework 2.0发布时工作,因此其中一些内容可能不再正确(如果是,请留下评论,我会进行更正)。
struct
)。enums
只是整数类型的包装器,而Java enums
几乎是完整的类(感谢Internet Friend的评论)。out
和ref
参数。还有其他语言差异,但大多数差异都未在字节码级别表示。例如,如果记忆无误,Java的非static
内部类(在.NET中不存在)不是字节码特性,编译器会为内部类的构造函数生成一个额外的参数并传递外部对象。.NET lambda表达式也是如此。
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 字节码的原因。不过这只是一个实现细节。
有些人说,Java 字节码是设计为多平台使用的,而 CIL 是专门为 Windows 设计的。但事实并非如此。.NET Framework 中有一些 "Windows" 主义,但在 CIL 中却没有。
作为上述第 4 点的例子,我以前写过一个玩具级的 Java 到 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
}
}
这是一个有效的CIL程序,可以输入到类似ilasm.exe
的CIL汇编器中以创建可执行文件。正如您所见,CIL是一种完全人类可读和可写的语言。您可以在任何文本编辑器中轻松地创建有效的CIL程序。
您还可以使用javac
编译器编译上面的Java程序,然后通过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 输出进行比较,你会发现它们非常相似。
它们本质上是在做相同的事情,MSIL是微软版本的Java字节码。
内部主要的区别有:
更多信息和详细比较可以在 K John Gough 的文章 (后缀文档)中找到。
CIL(通常称为MSIL)旨在是人可读的,而Java字节码则不是。
可以将Java字节码视为不存在的硬件的机器码(但JVM会模拟它)。
CIL更像汇编语言-距离机器码只有一步之遥,同时仍然可被人类读懂。
MSIL和Java字节码之间的概念非常相似。
我认为 MSIL 不应该与 Java 字节码进行比较,而是应该与“组成 Java 字节码的指令”进行比较。
没有反汇编后的 Java 字节码名称。“Java Bytecode”应该是一个非官方的别名,因为我在官方文档中找不到它的名称。 Java 类文件反汇编器 说
打印出反汇编代码,即类中每个方法所包含的 Java 字节码指令。这些指令在 Java 虚拟机规范中有记录。
“Java VM 指令”和“MSIL”都被组装成 .NET 字节码和 Java 代码,这些代码都无法被人类读取。
同意,对于初学者来说,这些差异足够微小,可以忽略不计。如果你想从基础开始学习 .Net,我建议先了解公共语言基础结构和公共类型系统。