Java虚拟机和CLR
作为对名为 MSIL 和 Java 字节码之间的差异?,Java 虚拟机的工作方式与 .NET Framework 公共语言运行时 (CLR) 的工作方式有何(主要)差异或相似之处?
另外,.NET Framework CLR 是“虚拟机”还是不具备虚拟机的属性?
As a sort of follow up to the question called Differences between MSIL and Java bytecode?, what is the (major) differences or similarity in how the Java Virtual Machine works versus how the .NET Framework Common Language Runtime (CLR) works?
Also, is the .NET framework CLR a "virtual machine" or does it not have the attributes of a virtual machine?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
两种实现之间有很多相似之处(在我看来:是的,它们都是“虚拟机”)。
一方面,它们都是基于堆栈的虚拟机,没有像我们习惯在 x86 或 PowerPC 等现代 CPU 中看到的“寄存器”概念。 所有表达式 ((1 + 1) / 2) 的计算是通过将操作数推入“堆栈”,然后每当指令(加法、除法等)需要消耗这些操作数时将这些操作数从堆栈中弹出来执行的。 每条指令将其结果推回堆栈。
这是实现虚拟机的一种便捷方法,因为世界上几乎每个CPU都有一个堆栈,但寄存器的数量通常不同(并且一些寄存器是专用的,并且每条指令期望其操作数位于不同的寄存器中,等等)。
因此,如果您要对抽象机进行建模,那么纯粹基于堆栈的模型是一个很好的方法。
当然,真实的机器不会这样操作。 因此,JIT 编译器负责执行字节码操作的“注册”,本质上是调度实际的 CPU 寄存器以尽可能包含操作数和结果。
所以,我认为这是 CLR 和 JVM 之间最大的共同点之一。
至于差异...
这两种实现之间的一个有趣的差异是 CLR 包含用于创建泛型类型,然后将参数专业化应用于这些类型的指令。 因此,在运行时,CLR 将 List视为一个列表。 是与 List完全不同的类型。
在幕后,它对所有引用类型特化使用相同的 MSIL(因此 List使用与 List
在 Java 中,泛型类型纯粹是编译器的技巧。 JVM 不知道哪些类具有类型参数,并且无法在运行时执行参数专门化。
从实际角度来看,这意味着您不能在泛型类型上重载 Java 方法。 您不能有两个具有相同名称的不同方法,仅在它们是否接受 List上有所不同。 或列表<日期>。 当然,由于 CLR 了解参数类型,因此它在处理泛型类型特化上重载的方法时没有问题。
在日常工作中,这是我最注意到的 CLR 和
JVM。
其他重要区别包括:
CLR 具有闭包(作为 C# 委托实现)。 JVM 仅从 Java 8 开始支持闭包。
CLR 具有协程(使用 C# 'yield' 关键字实现)。 JVM 没有。
CLR 允许用户代码定义新的值类型(结构体),而 JVM 提供固定的值类型集合(byte、short、int、long、float、double、char、boolean),并且只允许用户定义新的引用类型(类)。
CLR 提供对声明和操作指针的支持。 这一点特别有趣,因为 JVM 和 CLR 都采用严格的分代压缩垃圾收集器实现作为其内存管理策略。 在一般情况下,严格压缩 GC 对于指针来说确实很困难,因为当您将一个值从一个内存位置移动到另一个内存位置时,所有指针(以及指向指针的指针)都会变得无效。 但 CLR 提供了“固定”机制,以便开发人员可以声明不允许 CLR 移动某些指针的代码块。 这非常方便。
JVM 中最大的代码单元要么是一个“包”,如“protected”关键字所证明的,要么是一个 JAR(即 Java ARchive),如能够在类路径中指定一个 jar 并对其进行处理所证明的那样就像代码文件夹一样。 在 CLR 中,类被聚合到“程序集”中,并且 CLR 提供了用于推理和操作程序集的逻辑(这些程序集被加载到“AppDomains”中,为内存分配和代码执行提供子应用程序级沙箱)。
CLR 字节码格式(由 MSIL 指令和元数据组成)的指令类型比 JVM 少。 在 JVM 中,每个独特的操作(两个 int 值相加、两个 float 值相加等)都有自己独特的指令。 在 CLR 中,所有 MSIL 指令都是多态的(添加两个值),JIT 编译器负责确定操作数的类型并创建适当的机器代码。 但我不知道哪种策略是最好的。 两者都需要权衡。 对于 JVM,HotSpot JIT 编译器可以使用更简单的代码生成机制(它不需要确定操作数类型,因为它们已经编码在指令中),但这意味着它需要更复杂的字节码格式,指令类型更多。
我使用 Java(并且很欣赏 JVM)已经有大约十年了。
但是,在我看来,CLR 现在几乎在所有方面都是更优秀的实现。
There are a lot of similarities between both implementations (and in my opinion: yes, they're both "virtual machines").
For one thing, they're both stack-based VM's, with no notion of "registers" like we're used to seeing in a modern CPU like the x86 or PowerPC. The evaluation of all expressions ((1 + 1) / 2) is performed by pushing operands onto the "stack" and then popping those operands off the stack whenever an instruction (add, divide, etc) needs to consume those operands. Each instruction pushes its results back onto the stack.
It's a convenient way to implement a virtual machine, because pretty much every CPU in the world has a stack, but the number of registers is often different (and some registers are special-purpose, and each instruction expects its operands in different registers, etc).
So, if you're going to model an abstract machine, a purely stack-based model is a pretty good way to go.
Of course, real machines don't operate that way. So the JIT compiler is responsible for performing "enregistration" of bytecode operations, essentially scheduling the actual CPU registers to contain operands and results whenever possible.
So, I think that's one of the biggest commonalities between the CLR and the JVM.
As for differences...
One interesting difference between the two implementations is that the CLR includes instructions for creating generic types, and then for applying parametric specializations to those types. So, at runtime, the CLR considers a List<int> to be a completely different type from a List<String>.
Under the covers, it uses the same MSIL for all reference-type specializations (so a List<String> uses the same implementation as a List<Object>, with different type-casts at the API boundaries), but each value-type uses its own unique implementation (List<int> generates completely different code from List<double>).
In Java, generic types are a purely a compiler trick. The JVM has no notion of which classes have type-arguments, and it's unable to perform parametric specializations at runtime.
From a practical perspective, that means you can't overload Java methods on generic types. You can't have two different methods, with the same name, differing only on whether they accept a List<String> or a List<Date>. Of course, since the CLR knows about parametric types, it has no problem handling methods overloaded on generic type specializations.
On a day-to-day basis, that's the difference that I notice most between the CLR and the
JVM.
Other important differences include:
The CLR has closures (implemented as C# delegates). The JVM does support closures only since Java 8.
The CLR has coroutines (implemented with the C# 'yield' keyword). The JVM does not.
The CLR allows user code to define new value types (structs), whereas the JVM provides a fixed collection of value types (byte, short, int, long, float, double, char, boolean) and only allows users to define new reference-types (classes).
The CLR provides support for declaring and manipulating pointers. This is especially interesting because both the JVM and the CLR employ strict generational compacting garbage collector implementations as their memory-management strategy. Under ordinary circumstances, a strict compacting GC has a really hard time with pointers, because when you move a value from one memory location to another, all of the pointers (and pointers to pointers) become invalid. But the CLR provides a "pinning" mechanism so that developers can declare a block of code within which the CLR is not allowed to move certain pointers. It's very convenient.
The largest unit of code in the JVM is either a 'package' as evidenced by the 'protected' keyword or arguably a JAR (i.e. Java ARchive) as evidenced by being able to specifiy a jar in the classpath and have it treated like a folder of code. In the CLR, classes are aggregated into 'assemblies', and the CLR provides logic for reasoning about and manipulating assemblies (which are loaded into "AppDomains", providing sub-application-level sandboxes for memory allocation and code execution).
The CLR bytecode format (composed of MSIL instructions and metadata) has fewer instruction types than the JVM. In the JVM, every unique operation (add two int values, add two float values, etc) has its own unique instruction. In the CLR, all of the MSIL instructions are polymorphic (add two values) and the JIT compiler is responsible for determining the types of the operands and creating appropriate machine code. I don't know which is the preferably strategy, though. Both have trade-offs. The HotSpot JIT compiler, for the JVM, can use a simpler code-generation mechanism (it doesn't need to determine operand types, because they're already encoded in the instruction), but that means it needs a more complex bytecode format, with more instruction types.
I've been using Java (and admiring the JVM) for about ten years now.
But, in my opinion, the CLR is now the superior implementation, in almost every way.
您的第一个问题是将 JVM 与 .NET Framework 进行比较 - 我假设您实际上是想与 CLR 进行比较。 如果是这样,我认为您可以就此写一本小书(编辑: 看起来 Benji 已经有了:-)
一个重要的区别是 CLR 被设计为一种语言中立的体系结构,与JVM。
另一个重要的区别是 CLR 专门设计为允许与本机代码实现高水平的互操作性。 这意味着当访问和修改本机内存时,CLR 必须管理可靠性和安全性,并且还管理基于 CLR 的数据结构和本机数据结构之间的编组。
回答你的第二个问题,术语“虚拟机”是硬件世界中的一个较旧术语(例如 20 世纪 60 年代 IBM 的 360 虚拟化),它曾经意味着底层机器的软件/硬件模拟,以完成相同类型的任务。 VMWare 所做的事情。
CLR 通常被称为“执行引擎”。 在此上下文中,这是 x86 之上的 IL 机器的实现。 这也是 JVM 所做的事情,尽管您可能会认为 CLR 的多态字节码和 JVM 的类型化字节码之间存在重要区别。
所以你的第二个问题的迂腐答案是“不”。 但这实际上取决于您如何定义这两个术语。
编辑: JVM 和 CLR 之间的另一个区别是 JVM(版本 6)非常不愿意将分配的内存释放回操作系统,即使可以。
例如,假设 JVM 进程启动并最初从操作系统分配 25 MB 内存。 然后,应用代码尝试分配需要额外 50 MB 的空间。 JVM 将从操作系统分配额外的 50 MB 空间。 一旦应用程序代码停止使用该内存,它就会被垃圾收集,并且 JVM 堆大小将会减小。 但是,JVM 只会在某些非常特定的情况下。 否则,在进程生命周期的剩余时间内,该内存将保持分配状态。
另一方面,如果不再需要分配的内存,CLR 会将其释放回操作系统。 在上面的示例中,一旦堆减少,CLR 就会释放内存。
Your first question is comparing the JVM with the .NET Framework - I assume you actually meant to compare with the CLR instead. If so, I think you could write a small book on this (EDIT: looks like Benji already has :-)
One important difference is that the CLR is designed to be a language-neutral architecture, unlike the JVM.
Another important difference is that the CLR was specifically designed to allow for a high level of interoperability with native code. This means that the CLR must manage reliability and security when native memory is accessed and modified, and also manage marshalling between CLR-based data structures and native data structures.
To answer your second question, the term “virtual machine” is an older term from the hardware world (e.g. IBM’s virtualisation of the 360 in the 1960s) that used to mean a software/hardware emulation of the underlying machine to accomplish the same sort of stuff that VMWare does.
The CLR is often referred to as an "execution engine". In this context, that's an implementation of an IL Machine on top of an x86. This is also what the JVM does, although you can argue that there's an important difference between the CLR's polymorphic bytecodes and the JVM's typed bytecodes.
So the pedantic answer to your second question is "no". But it really comes down to to how you define these two terms.
EDIT: One more difference between the JVM and the CLR is that the JVM (version 6) is very reluctant to release allocated memory back to the operating system, even where it can.
For example, let's say that a JVM process starts and allocates 25 MB of memory from the operating system initially. The app code then attempts allocations that require an additional 50 MB. The JVM will allocate an additional 50 MB from the operating system. Once the application code has stopped using that memory, it is garbage-collected and the JVM heap size will decrease. However, the JVM will only free the allocated operating system memory under certain very specific circumstances. Otherwise, for the rest of the process lifetime that memory will remain allocated.
The CLR, on the other hand, releases allocated memory back to the operating system if it's no longer needed. In the example above, the CLR would have released the memory once the heap had decreased.
CLR和JVM都是虚拟机。
.NET Framework 和 Java 运行时环境是各自 VM 及其库的捆绑。 如果没有库,虚拟机就毫无用处。
The CLR and the JVM are both virtual machines.
The .NET Framework and the Java Runtime Environment are the bundling of the respective VMs and their libraries. Without libraries the VMs are pretty useless.
有关差异的更多细节可以从各种学术和私人来源找到。 CLR 设计选择就是一个很好的例子。
一些具体示例包括:
More specifics on the differences can be found at from various academic and private sources. Once good example is CLR Design Choices.
Some specific examples include:
它不是虚拟机,.net 框架在第一次运行时将程序集编译为本机二进制文件:
在计算中,即时编译 (JIT),也称为动态翻译,是一种改进程序集的技术。计算机程序的运行时性能。 JIT 建立在运行时环境中的两个早期思想之上:字节码编译和动态编译。 它在本地执行之前在运行时将代码转换为本地机器代码。 相对于解释器的性能改进源于缓存翻译代码块的结果,而不是简单地在每次满足时重新评估每一行或操作数(请参阅解释语言)。 与在开发时静态编译代码相比,它还具有优势,因为如果发现有利的话,它可以重新编译代码,并且可以强制执行安全保证。 因此,JIT 可以结合解释和静态(提前)编译的一些优点。
一些现代运行时环境(例如 Microsoft 的 .NET Framework、大多数 Java 实现以及最近的 Actionscript 3)都依赖 JIT 编译来实现高速代码执行。
来源:http://en.wikipedia.org/wiki/Just-in-time_compilation< /a>
添加.NET框架包含一个虚拟机,就像Java一样。
It is not a virtual machine, the .net framework compiles the assemblies into native binary at the time of the first run:
In computing, just-in-time compilation (JIT), also known as dynamic translation, is a technique for improving the runtime performance of a computer program. JIT builds upon two earlier ideas in run-time environments: bytecode compilation and dynamic compilation. It converts code at runtime prior to executing it natively, for example bytecode into native machine code. The performance improvement over interpreters originates from caching the results of translating blocks of code, and not simply reevaluating each line or operand each time it is met (see Interpreted language). It also has advantages over statically compiling the code at development time, as it can recompile the code if this is found to be advantageous, and may be able to enforce security guarantees. Thus JIT can combine some of the advantages of interpretation and static (ahead-of-time) compilation.
Several modern runtime environments, such as Microsoft's .NET Framework, most implementations of Java, and most recently Actionscript 3, rely on JIT compilation for high-speed code execution.
Source: http://en.wikipedia.org/wiki/Just-in-time_compilation
Adding up .NET framework contains a virtual machine, just like Java.