Java的虚拟机和CLR


140

作为对MSIL和Java字节码之间差异的问题的一种跟进,Java虚拟机的工作方式与Java虚拟机的主要(主要)区别或相似之处是什么.NET Framework 公共语言运行库(CLR)是否有效?

另外,是 .NET框架 CLR是“虚拟机”还是不具有虚拟机的属性?


好吧,如果您要按喜欢进行比较,则应将问题改写为VM与CLR(公共语言运行时)之间的差异,这是VM的直接类比。
cletus

Answers:


278

两种实现之间有很多相似之处(我认为:是的,它们都是“虚拟机”)。

一方面,它们都是基于堆栈的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#委托实现)。JVM仅从Java 8开始才支持闭包。

  • CLR具有协程(通过C#'yield'关键字实现)。JVM没有。

  • CLR允许用户代码定义新的值类型(结构),而JVM提供固定的值类型集合(字节,短型,整型,长型,浮点型,双精度型,字符型,布尔型),并且仅允许用户定义新的引用-类型(类)。

  • CLR提供了声明和操作指针的支持。这特别有趣,因为JVM和CLR均采用严格的世代压缩垃圾收集器实现作为其内存管理策略。在通常情况下,严格的压缩GC很难使用指针,因为将值从一个存储位置移动到另一个存储位置时,所有指针(和指向指针的指针)都将变为无效。但是CLR提供了一种“固定”机制,以便开发人员可以声明一个代码块,在其中不允许CLR移动某些指针。非常方便

  • JVM中最大的代码单元是“保护”关键字所证明的“包”,或者是能够在类路径中指定jar并将其像文件夹一样证明的JAR(即Java ARchive)。代码。在CLR中,类被聚合为“程序集”,而CLR提供推理和操纵程序集的逻辑(程序集被加载到“ AppDomains”中,提供子应用程序级沙箱用于内存分配和代码执行)。

  • CLR字节码格式(由MSIL指令和元数据组成)具有比JVM少的指令类型。在JVM中,每个唯一操作(添加两个int值,添加两个float值等)都有自己的唯一指令。在CLR中,所有MSIL指令都是多态的(加两个值),而JIT编译器负责确定操作数的类型并创建适当的机器代码。我不知道哪个是最好的策略。两者都有权衡。适用于JVM的HotSpot JIT编译器可以使用更简单的代码生成机制(它不需要确定操作数类型,因为它们已经在指令中编码了),但这意味着它需要更复杂的字节码格式,具有更多指令类型。

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

但是,我认为,几乎在所有方面,CLR都是卓越的实现。


73
闭包和生成器在语言级别实现,并且在CLR级别上简单表示为类。
Curt Hagenlocher,2009年

2
他们处理堆的方式有何不同?CLR更依赖于OS / host进程,而JVM或多或少地完全管理堆内存。
法国人Kelly S.

6
一个重要的区别是即时编译(CLR)与(Oracle / Sun)JVM中的自适应优化之间的对比。
Edwin Dalorzo 2012年

1
Java的局部变量槽的行为与寄存器非常相似。但是,自从JIT将本地插槽和堆栈转换为真实寄存器以来,这一切都没有意义。
锑2013年

1
@kuhajeyan这是因为引入CLR时,JVM已有10年的历史了。这在IT中是很长的时间。当JVM在1993年问世时,还没有一个真正的竞争者,对于CLR(2003),有一个成熟而扎实的JVM,在行业中有很强的立足点。
简单的研究员,

25

您的第一个问题是将JVM与.NET Framework进行比较-我假设您实际上是要与CLR进行比较。如果是这样,我想您可以为此写一本小书(编辑:看来Benji已经有了:-)

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

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

为了回答您的第二个问题,术语“虚拟机”是硬件世界中的一个较旧术语(例如,IBM在1960年代对360进行虚拟化),它表示对底层机器进行相同类型的软件/硬件仿真。 VMWare做的事情。

CLR通常被称为“执行引擎”。在这种情况下,这是在x86之上的IL机器的实现。这也是JVM所做的,尽管您可以辩称CLR的多态字节码与JVM的类型化字节码之间存在重要区别。

因此,您对第二个问题的学问答案是“否”。但这实际上取决于您如何定义这两个术语。

编辑: JVM和CLR之间的另一个区别是,JVM(版本6)非常不愿意将分配的内存释放回操作系统,即使可以的话。

例如,假设JVM进程开始并从操作系统最初分配25 MB内存。然后,该应用程序代码尝试分配需要额外50 MB的内存。JVM将从操作系统中另外分配50 MB。一旦应用程序代码停止使用该内存,便会对其进行垃圾回收,并且JVM堆大小将减小。但是,JVM仅在某些非常特定的情况下才会释放分配的操作系统内存。否则,在剩余的进程生命周期中,该内存将保持分配状态。

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


2
JVM不会释放分配的内存是绝对不正确的。见我回答这个问题的证明:stackoverflow.com/questions/366658/...
迈克尔博格瓦特

我已经看到JVM将内存返回到Windows。
Steve Kuo

我更改了答案,说JVM 6非常不愿意释放内存,并带有Ran和Michael的答案的链接。我从未在JVM 5上看到过这种行为,所以也许该版本更加不情愿。
HTTP 410

您能否介绍CLR依赖父进程时JVM如何主动管理堆?我使用的特定示例是JVM具有最大堆大小的运行时参数,而默认CLR环境则没有。确实,托管在IIS下的CLR应用程序可以将IIS配置为限制内存,这意味着在虚拟机的定义中包括IIS。
凯利·法国

@Steve Kuo,是的,我也看到过。通常在下午5点到下午6点之间。


11

可以从各种学术和私人来源中找到有关差异的更多详细信息。一个很好的例子就是CLR Design Choices

一些具体示例包括:

  • 键入了一些低级操作数,例如“加两个整数”,其中CLR使用多态操作数。(即fadd / iadd / ladd vs just add)
  • 当前,JVM进行更具综合性的运行时性能分析和优化(即Hotspot)。CLR当前执行JIT优化,但不进行运行时优化(即在运行时替换代码)。
  • CLR不会内联虚拟方法,JVM会...
  • 对CLR中的值类型的支持不仅仅限于“原语”。

-11

它不是虚拟机,.net框架在第一次运行时将程序集编译为本地二进制文件:

在计算中,即时编译(JIT)(也称为动态翻译)是一种用于提高计算机程序的运行时性能的技术。JIT基于运行时环境中的两个较早的想法:字节码编译和动态编译。它在本地执行代码之前先在运行时将其转换,例如将字节码转换为本地机器代码。与解释器相比,性能的提高源自缓存代码块翻译的结果,而不是每次遇到行或操作数时都要重新评估(请参见解释性语言)。与在开发时静态编译代码相比,它还具有优势,因为如果发现这样做是有利的,它可以重新编译代码,并且可以执行安全保证。

几种现代的运行时环境,例如Microsoft的.NET Framework,大多数Java实现以及最近的Actionscript 3,都依赖JIT编译来执行高速代码。

资料来源:http : //en.wikipedia.org/wiki/Just-in-time_compilation

像Java一样,添加.NET框架包含一个虚拟机。


10
仅仅因为虚拟机利用JIT进行了性能优化,并不意味着它不再是虚拟机。当程序员进行编译时,他将编译到虚拟机上,由实现来执行,但执行起来却
不可行
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.