Java的虚拟机和CLR
作为对 MSIL 和 Java 字节码之间的差异问题的补充?,Java 虚拟机的工作方式与 .NET Framework 公共语言运行时 (CLR) 的工作方式有什么(主要)差异或相似之处?
此外,.NET 框架 CLR 是“虚拟机”还是没有虚拟机的属性?
作为对 MSIL 和 Java 字节码之间的差异问题的补充?,Java 虚拟机的工作方式与 .NET Framework 公共语言运行时 (CLR) 的工作方式有什么(主要)差异或相似之处?
此外,.NET 框架 CLR 是“虚拟机”还是没有虚拟机的属性?
这两种实现之间有很多相似之处(在我看来:是的,它们都是“虚拟机”)。
首先,它们都是基于堆栈的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中最大的代码单元要么是“包”,如“protected”关键字所示,也可以说是JAR(即Java ARchive),这可以通过在类路径中指定jar并将其视为代码文件夹来证明。在 CLR 中,类聚合到“程序集”中,CLR 提供用于推理和操作程序集的逻辑(程序集加载到“AppDomains”中,为内存分配和代码执行提供子应用程序级沙箱)。
CLR 字节码格式(由 MSIL 指令和元数据组成)的指令类型比 JVM 少。在JVM中,每个唯一的操作(添加两个int值,添加两个浮点值等)都有自己唯一的指令。在 CLR 中,所有 MSIL 指令都是多态的(添加两个值),JIT 编译器负责确定操作数的类型并创建适当的机器代码。不过,我不知道哪个是更可取的策略。两者都有权衡。对于JVM,HotSpot JIT编译器可以使用更简单的代码生成机制(它不需要确定操作数类型,因为它们已经在指令中编码),但这意味着它需要更复杂的字节码格式,具有更多的指令类型。
我已经使用Java(并欣赏JVM)大约十年了。
但是,在我看来,CLR现在几乎是所有方面的高级实现。
你的第一个问题是将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 就会释放内存。