Java的虚拟机和CLR

问题

作为对问题Differences between MSIL and Java bytecode?的一种跟进,Java虚拟机如何工作与.NET FrameworkCommon语言运行时(CLR)如何工作的(主要)差异或相似之处是什么?

另外,.NET frameworkCLR是"虚拟机"还是没有虚拟机的属性?


#1 热门回答(263 赞)

两种实现之间有很多相似之处(在我看来:是的,它们都是"虚拟机")。

首先,它们都是基于堆栈的VM,没有"寄存器"的概念,就像我们习惯于在x86或PowerPC等现代CPU中看到的那样。所有表达式((1 1)/ 2)的评估是通过将操作数推入"堆栈"然后在指令(添加,除法等)需要消耗这些操作数时从堆栈中弹出这些操作数来执行的。每条指令将其结果推回堆栈。

这是实现虚拟机的一种便捷方式,因为世界上几乎每个CPU都有一个堆栈,但寄存器的数量通常是不同的(有些寄存器是专用的,每个指令都要求它的操作数在不同的寄存器中等等)。

因此,如果你要为抽象机器建模,那么纯粹的基于堆栈的模型是一个非常好的方法。

当然,真正的机器不能以这种方式运行。因此,JIT编译器负责执行字节码操作的"注册",实质上是调度实际的CPU寄存器以尽可能包含操作数和结果。

所以,我认为这是CLR和JVM之间最大的共性之一。

至于差异......

这两种实现之间的一个有趣的区别是CLR包含用于创建泛型类型的指令,然后用于将参数化特化应用于这些类型。因此,在运行时,CLR认为List <int>是与List <String>完全不同的类型。

在封面下,它对所有引用类型的特化使用相同的MSIL(因此List <String>使用与List <Object>相同的实现,在API边界使用不同的类型转换),但每个值类型使用它自己的唯一实现(List <int>生成与List <double>完全不同的代码)。

在Java中,泛型类型纯粹是一种编译技巧。 JVM没有关于哪些类具有类型参数的概念,并且它无法在运行时执行参数化特化。

从实际角度来看,这意味着你不能在泛型类型上重载Java方法。你不能使用具有相同名称的两种不同方法,区别在于它们是接受List <String>还是List <Date>。当然,由于CLR知道参数类型,因此在泛型类型特化上重载处理方法没有问题。

在日常的基础上,这是我在CLR和JVM之间最常见的区别。

其他重要的差异包括:

  • CLR有闭包(实现为C#代理)。自Java 8以来,JVM确实支持闭包。
  • CLR具有协同程序(使用C#'yield'关键字实现)。 JVM没有。
  • CLR允许用户代码定义新的值类型(结构),而JVM提供固定的值类型集合(byte,short,int,long,float,double,char,boolean),并且只允许用户定义新的引用-types(类)。
  • CLR支持声明和操作指针。这一点特别有趣,因为JVM和CLR都采用严格的代压缩垃圾收集器实现作为其内存管理策略。在通常情况下,严格压缩GC的指针非常困难,因为当你将值从一个内存位置移动到另一个内存位置时,所有指针(和指针指针)都将变为无效。但CLR提供了一种"固定"机制,以便开发人员可以声明一个代码块,在该代码块内不允许CLR移动某些指针。这很方便。
  • JVM中最大的代码单元是一个'包',可以通过'protected'关键字或可以说是一个JAR(即Java ARchive)来证明,这可以通过在类路径中指定jar并将其视为一个jar来证明代码文件夹。在CLR中,类被聚合到"程序集"中,CLR提供用于推理和操作程序集的逻辑(它们被加载到"AppDomains"中,为内存分配和代码执行提供子应用程序级沙箱)。
  • CLR字节码格式(由MSIL指令和元数据组成)具有比JVM更少的指令类型。在JVM中,每个唯一的操作(添加两个int值,添加两个浮点值等)都有自己独特的指令。在CLR中,所有MSIL指令都是多态的(添加两个值),JIT编译器负责确定操作数的类型并创建适当的机器代码。不过,我不知道哪个是优选策略。两者都有权衡。用于JVM的HotSpot JIT编译器可以使用更简单的代码生成机制(它不需要确定操作数类型,因为它们已经在指令中编码),但这意味着它需要更复杂的字节码格式,更多指令类型。

我已经使用Java(并欣赏JVM)已有十年了。

但是,在我看来,CLR现在是优秀的实现,在几乎所有方面。


#2 热门回答(25 赞)

你的第一个问题是将JVM与.NET Framework进行比较 - 我假设你实际上想要与CLR进行比较。如果是这样的话,我想你可以写一本关于此的小书(**编辑:**像Benji已经看到的那样:-)

一个重要的区别是,与JVM不同,CLR被设计为与语言无关的架构。

另一个重要的区别是CLR专门设计用于实现与本机代码的高度互操作性。这意味着CLR必须在访问和修改本机内存时管理可靠性和安全性,并且还必须在基于CLR的数据结构和本机数据结构之间管理manage marshalling

为了回答你的第二个问题,术语"虚拟机"是硬件世界中较早的术语(例如,IBM在20世纪60年代对360的虚拟化),这些术语过去是指底层机器的软件/硬件仿真,以实现相同的类型。 VMWare的功能。

CLR通常被称为"执行引擎"。在这种情况下,这是在x86之上的IL机器的实现。这也是JVM的作用,尽管你可以说CLR的多态字节码和JVM的类型字节码之间存在重要差异。

所以你第二个问题的迂腐回答是"不"。但这实际上取决于你如何定义这两个术语。

**编辑:**JVM和CLR之间的另一个区别是JVM(版本6)是very reluctant,将已分配的内存释放回操作系统,即使它可以。

例如,假设JVM进程最初启动并从操作系统分配25 MB内存。然后,应用程序代码尝试分配需要额外50 MB的分配。 JVM将从操作系统分配额外的50 MB。一旦应用程序代码停止使用该内存,它就会被垃圾收集,并且JVM堆大小将减少。但是,JVM将仅在某些情况下释放已分配的操作系统内存very specific circumstances。否则,对于剩余的进程生命周期,内存将保持分配状态。

另一方面,如果不再需要CLR,则将已分配的内存释放回操作系统。在上面的示例中,一旦堆减少,CLR就会释放内存。


#3 热门回答(11 赞)

可以从各种学术和私人来源找到关于差异的更多细节。曾经很好的例子是CLR Design Choices

一些具体的例子包括:

  • 键入一些低级别的opperands,例如"添加两个整数",其中CLR使用多态操作数。 (即fadd / iadd / ladd vs just add)
  • 目前,JVM进行更具侵略性的运行时分析和优化(即Hotspot)。 CLR目前进行JIT优化,但不进行运行时优化(即在运行时替换代码)。
  • CLR没有内联虚拟方法,JVM确实......
  • 支持CLR中的值类型,而不仅仅是"原语"。