.net CLR 是虚拟机吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1564348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is the CLR a virtual machine?
提问by this. __curious_geek
I read a book which referred to the .net CLR as a virtual machine? Can anyone justify this? What is the reason we need the concept of virtual machines on some development platforms?
我读了一本将 .net CLR 称为虚拟机的书?任何人都可以证明这一点吗?我们在某些开发平台上需要虚拟机概念的原因是什么?
Isn't it possible to develop a native framework [one without virtual machine] that is fully object oriented and as powerful as .net?
难道不能开发一个完全面向对象且与.net 一样强大的本机框架[一个没有虚拟机的框架]?
The book which refers to CLR as virtual machine is "Professional .Net Framework 2.0".
将 CLR 称为虚拟机的书是“ Professional .Net Framework 2.0”。
回答by Joel Coehoorn
There are a lot of misconceptions here. I suppose you could think of .Net as a virtual machine if you really wanted, but let's look at how the .Net Framework really handles your code. The typical scenario looks like this
这里有很多误解。我想如果您真的愿意,您可以将 .Net 视为虚拟机,但让我们看看 .Net 框架如何真正处理您的代码。典型的场景是这样的
- You write a .Net program in C#, VB.Net, F#, or some other compatible language.
- That code is compiled down to an Intermediate Language (IL), which is similar to Java's bytecode, that is distributed to end-user machines.
- An end user invokes the program for the first time on a computer with the right version of .Net installed
- The computer sees this is a .Net assembly rather than "raw" machine code, and passes it off to the JIT compiler
- The JIT compiler compiles the IL to fully-native machine code.
- The native code is saved in memory for the life of this program execution.
- The saved native code is invoked, and the IL no longer matters.
- 您使用 C#、VB.Net、F# 或其他兼容语言编写 .Net 程序。
- 该代码被编译为中间语言 (IL),类似于 Java 的字节码,分发给最终用户机器。
- 最终用户在安装了正确版本的 .Net 的计算机上首次调用该程序
- 计算机看到这是一个 .Net 程序集而不是“原始”机器代码,并将其传递给 JIT 编译器
- JIT 编译器将 IL 编译为完全本机的机器代码。
- 本机代码在此程序执行的生命周期中保存在内存中。
- 调用保存的本机代码,IL 不再重要。
There are a couple important points here, but the big one is that at no point is any code ever interpreted. Instead, you can see in step 5 that it is compiled to native code. This a hugedifference than loading it into a virtual machine, for several reasons:
这里有几个要点,但最重要的一点是任何代码都没有被解释过。相反,您可以在第 5 步中看到它已编译为本机代码。这与将其加载到虚拟机中的差异很大,原因如下:
- The fully-compiled code is executed by the cpu directly rather than interpreted or translated by an additional software abstraction layer, which should be faster.
- The JIT compiler can take advantage of optimizations specific to the individual machine running the program, rather than settling for a lowest common denominator.
- If you want you can even pre-compile the code and in essence hide step 5 from the user completely.
- 完全编译的代码由cpu直接执行,而不是由额外的软件抽象层解释或翻译,应该更快。
- JIT 编译器可以利用特定于运行程序的单个机器的优化,而不是满足最低公分母。
- 如果您愿意,您甚至可以预编译代码,并在本质上对用户完全隐藏第 5 步。
I suppose you could call this a virtual machine, in the sense the JITter abstracts away the details of the real machine from the developer. Personally I don't think that's really right, because for many people, a virtual machine implies a runtime abstraction away from native code that for .Net programs just doesn't exist.
我想您可以将其称为虚拟机,因为 JITter 从开发人员那里抽象出了真实机器的细节。就我个人而言,我认为这并不正确,因为对于许多人来说,虚拟机意味着远离本机代码的运行时抽象,而对于 .Net 程序来说,这种抽象是不存在的。
One other key point about this whole process that really sets it apart from a "virtual machine" environment is that it's only the typicalprocess. If you really want to, you can pre-compile a .Net assembly before distribution and deploy native code directly to end users (hint: it's slower in aggregate over the life of the program, because you lose machine-specific optimizations). Of course, you still need the .Net runtime installed, but at this point it's really not much different from any other runtime API; it's more like a collection dlls with a nice API you can link against, as you might have with the VB or C runtimes Microsoft also ships with Visual Studio. This kind of takes the IL out of the picture, making the VM moniker much harder to justify. (I say "kind of" because the IL is still deployed and used to verify the saved code, but it's never itself touched for execution).
整个过程真正区别于“虚拟机”环境的另一个关键点是它只是典型的过程。如果您真的想要,您可以在分发之前预编译一个 .Net 程序集,并将本机代码直接部署给最终用户(提示:在程序的整个生命周期中,它的聚合速度较慢,因为您失去了特定于机器的优化)。当然,您仍然需要安装 .Net 运行时,但在这一点上,它与任何其他运行时 API 没有太大区别;它更像是一个带有可以链接的漂亮 API 的集合 dll,就像 Microsoft 随 Visual Studio 一起提供的 VB 或 C 运行时一样。这种将 IL 排除在外,使 VM 绰号更难以证明。(我说“有点”是因为 IL 仍然被部署并用于验证保存的代码,但它本身从未被触及以执行)。
One other telling point is the lack of a VM process. When you run your app, there's no common "sandbox" process that runs. Compare this with Java, where if you open the task manager when a program is running you will see a process specifically for the Java VM, and the application's actual process is a thread inside of the sandbox created by the VM. In .Net, you see the application's process in the Windows task manager directly.
另一个明显的问题是缺少 VM 进程。当您运行您的应用程序时,没有运行常见的“沙箱”进程。将其与 Java 进行比较,如果在程序运行时打开任务管理器,您将看到一个专门针对 Java VM 的进程,而应用程序的实际进程是 VM 创建的沙箱内的线程。在 .Net 中,您可以直接在 Windows 任务管理器中看到应用程序的进程。
In summary: you could say that IL + CLR + JIT together somehow make up a virtual machine. Personally I don't think so, but I won't argue with you if you believe that. The point I want to make is that when you tell someone that .Net runs in a virtual machine with no further explanation, the idea you are communicating to that person is "interpreted bytecode in a host process." And that's just wrong.
总结:您可以说 IL + CLR + JIT 以某种方式共同组成了一个虚拟机。我个人不这么认为,但如果你相信,我不会与你争论。我想说明的一点是,当您告诉某人 .Net 在虚拟机中运行而没有进一步解释时,您与该人交流的想法是“在主机进程中解释字节码”。这是错误的。
回答by Heath Hunnicutt
Similar to the Java Virtual Machine (JVM), the .net CLR is a byte-code interpreting virtual machine.
类似于 Java 虚拟机 (JVM),.net CLR 是一个字节码解释虚拟机。
The JVM interprets programs which contain java byte codes and the .net CLR interprets programs which contain what Microsoft calls "Intermediate Language (IL)" instructions. There are differences between these byte codes, but the virtual machines are similar and aspire to provide similar features.
JVM 解释包含 java 字节码的程序,而 .net CLR 解释包含 Microsoft 所谓的“中间语言 (IL)”指令的程序。这些字节码之间存在差异,但虚拟机是相似的,并渴望提供相似的功能。
Both of these virtual machine implementations have the ability to compile their input bytecode to the machine language of the computer they are running on. This is called "Just In Time Compilation (JIT)"and the output code produced is called "JIT code." Because the JIT code contain sequences of instructions in the machine language of the computer's CPU, this code is sometimes referred to as "native" code.
这两种虚拟机实现都能够将它们的输入字节码编译成它们正在运行的计算机的机器语言。这称为“即时编译 (JIT)”,生成的输出代码称为“JIT 代码”。由于 JIT 代码包含计算机 CPU 机器语言的指令序列,因此此代码有时称为“本机”代码。
However, JIT code is qualitatively and quantitatively different from native code, as explained below. For that reason, this article considers JIT code to be nothing more than a native implementation of the Virtual Machine while running a particular bytecode program.
但是,JIT 代码在质量和数量上都不同于本机代码,如下所述。出于这个原因,本文认为 JIT 代码只不过是运行特定字节码程序时虚拟机的本机实现。
One feature that both these Virtual Machines (VMs) aspire to provide is security in the form of preventing certain hazardous programming errors. For example, the title of this website forum, stackoverflow, is inspired by one such type of hazardous error that is possible in native code.
这些虚拟机 (VM) 都希望提供的一项功能是以防止某些危险编程错误的形式提供安全性。例如,本网站论坛的标题 stackoverflow 的灵感来自于本机代码中可能存在的一种此类危险错误。
In order to provide safety and execution security, the VMs implement type safety at the "Virtual Machine level". Assignments to VM memory are required to store the type of data which is held in that memory location. For example, if an integer is pushed onto the stack, it is not possible to pop a double from the stack. C-style "unions" are prohibited. Pointers and direct access to memory are prohibited.
为了提供安全性和执行安全性,VM 在“虚拟机级别”实现类型安全。需要分配给 VM 内存以存储该内存位置中保存的数据类型。例如,如果一个整数被压入堆栈,则不可能从堆栈中弹出一个双精度值。禁止 C 风格的“联合”。禁止指针和直接访问内存。
We could not get the same benefits by enforcing an object oriented language framework on developers if the result is a native binary such as an EXE file. In that case, we would not be able to distinguish between native binaries generated using the framework and EXEs generated by a malicious user employing sources other than the framework.
如果结果是本机二进制文件(如 EXE 文件),则我们无法通过对开发人员实施面向对象的语言框架来获得相同的好处。在这种情况下,我们将无法区分使用框架生成的本机二进制文件和恶意用户使用框架以外的来源生成的 EXE。
In the case of the VMs, the type-safety is enforced at the "lowest level" that the programmer is allowed to access. (Neglecting for a moment that it is possible to write managed native code, that is.) Therefore, no user will encounter an application which performs one of the hazardous operations which require direct access to memory locations and pointers.
在 VM 的情况下,类型安全是在允许程序员访问的“最低级别”强制执行的。(暂时忽略了可以编写托管本机代码。)因此,没有用户会遇到执行需要直接访问内存位置和指针的危险操作之一的应用程序。
In practice, the .net CLR implements a way to write native code which can be called by .net "managed" code. In this case, the burden is on the native code author not to make any of the pointer and memory mistakes.
在实践中,.net CLR 实现了一种编写本机代码的方法,该代码可由 .net“托管”代码调用。在这种情况下,本机代码作者的负担是不要犯任何指针和内存错误。
As both the JVM and .net CLR perform JIT compilation, either VM actually creates a native-compiled binary from the bytecode supplied. This "JIT code" performs more quickly than the VM's interpreter execution, because even the machine language code produced by JIT contains all the VM's needed safety checks that the VM would perform. As a result, the JIT output code is not as fast as native code which would ordinarily not contain numerous run-time checks. However, this speed performance drawback is exchanged for an improvement to reliability including security; in particular, use of uninitialized storage is prevented, type-safety of assignments is enforced, range-checking is performed (thus stack- and heap- based buffer overflows prevented), object lifetimes are managed by garbage collection, dynamic allocation is type safe. An environment executing such run-time behavior checks is implementing the specification of a virtual machine and is little more than a machine language realization of a virtual machine.
由于 JVM 和 .net CLR 都执行 JIT 编译,因此任何一个 VM 实际上都会根据提供的字节码创建本地编译的二进制文件。这种“JIT 代码”的执行速度比 VM 的解释器执行速度更快,因为即使是 JIT 生成的机器语言代码也包含 VM 将执行的所有 VM 所需的安全检查。因此,JIT 输出代码不如本机代码快,后者通常不包含大量运行时检查。然而,这种速度性能缺陷被交换为包括安全性在内的可靠性改进;特别是,禁止使用未初始化的存储,强制赋值类型安全,执行范围检查(从而防止基于堆栈和堆的缓冲区溢出),对象生命周期由垃圾收集管理,动态分配是类型安全的。
回答by noctonura
The "Virtual Machine" part refers to the fact that .NET code is compiled into EXE's and DLL's as "Intermediate" Assembly language (IL) to run on a virtual machine, as opposed to real CPU assembly language. Then, at runtime the ILM is converted into real CPU assembly for execution (referred to as Just-in-time, or JIT compiling).
“虚拟机”部分是指 .NET 代码被编译成 EXE 和 DLL 作为“中间”汇编语言 (IL) 以在虚拟机上运行,而不是真正的 CPU 汇编语言。然后,在运行时将 ILM 转换为真正的 CPU 程序集以供执行(称为即时或 JIT 编译)。
Sure, you could write a .NET compiler so that it would be compiled into CPU assembly language instead of IL. However, this would not be portable to all CPUs - you'd have to to compile a different version for each OS/CPU pair. But by compiling into ILM, you let the "Virtual Machine" handle the CPU and OS specific stuff.
当然,您可以编写一个 .NET 编译器,以便将其编译为 CPU 汇编语言而不是 IL。但是,这不能移植到所有 CPU - 您必须为每个 OS/CPU 对编译不同的版本。但是通过编译到 ILM,您可以让“虚拟机”处理 CPU 和操作系统特定的东西。
回答by dreadwail
The advantage of the CLR is the freedom to write code in whatever programming language the developer chooses, since the code will be compiled down to CLR before being interpreted into native calls. The .NET framework uses this JIT compilation to treat everything uniformly and output programs which work for the platform being deployed on, which is absent from compiled languages.
CLR 的优点是可以自由地使用开发人员选择的任何编程语言编写代码,因为代码在被解释为本地调用之前将被编译为 CLR。.NET 框架使用这种 JIT 编译来统一处理所有内容并输出适用于正在部署的平台的程序,这是编译语言所没有的。
回答by Andrew Keith
I am a bit old school, so i call the CLR a virtual machine as well. My reasoning is that the CLR assembles the machine code from an intermediate bytecode, which is what a virtual machine also does.
我有点老派,所以我也称 CLR 为虚拟机。我的推理是 CLR 从中间字节码组装机器代码,这也是虚拟机所做的。
The benefits of the CLR is mainly due to the way it assembles the machine code which utilizes runtime type information.
CLR 的优势主要在于它使用运行时类型信息组装机器代码的方式。
You can develop a native framework as powerful as the .NET framework using just native types. The only flexibility you lose is the ability to reassemble the native code if you ever transport your program to another platform without recompiling.
您可以仅使用本机类型开发与 .NET 框架一样强大的本机框架。如果您将程序传输到另一个平台而无需重新编译,那么您失去的唯一灵活性是重新组装本机代码的能力。
回答by user2809447
Neither the JVM nor the CLR do anything that is materially different than what most "virtual machines" for other languages also do. Modernly, they all use JIT to convert virtual instructions (p-code, bytecodes, intermediate language instructions, call it whatever you like) to "native CPU hardware" instructions ("machine code.")
JVM 和 CLR 都没有做任何与其他语言的大多数“虚拟机”也做的事情有本质不同的事情。现代,它们都使用 JIT 将虚拟指令(p 代码、字节码、中间语言指令,你喜欢怎么称呼)转换为“本地 CPU 硬件”指令(“机器代码”)。
In fact, the first "virtual machine" to do this was the Smalltalk virtual machine. The author of that innovation, Peter Deutsch, dubbed it "dynamic translation" instead of the term "JIT," which was popularized by Java. If the Smalltalk "runtime execution environment" is going to be called a "virtual machine" (and that's what it's still called,) then any and all other "run time systems" that do essentially the same thing also qualify as "virtual machines."
事实上,第一个做到这一点的“虚拟机”是 Smalltalk 虚拟机。这项创新的作者 Peter Deutsch 将其称为“动态翻译”,而不是由 Java 普及的术语“JIT”。如果 Smalltalk“运行时执行环境”将被称为“虚拟机”(它仍然被称为),那么任何和所有其他基本上做相同事情的“运行时系统”也有资格作为“虚拟机”。 ”
回答by Niki
You've got many valuable answers, but I think one thing hasn't been mentioned yet: Modularity.
你有很多有价值的答案,但我认为还没有提到一件事:模块化。
It's quite hard to export a OO class from native DLL. Sure, you can tell the linker to export the class and import it somewhere else, but this is brittle; Changing a single private member in a class will break binary compatibility, i.e. if you change one DLL without recompiling all the other modules, your program will crash horribly at runtime.
从本机 DLL 导出 OO 类非常困难。当然,您可以告诉链接器导出类并将其导入其他地方,但这很脆弱;更改类中的单个私有成员将破坏二进制兼容性,即如果您更改一个 DLL 而不重新编译所有其他模块,您的程序将在运行时崩溃。
There are some ways around this: For example, you can define public abstract interfaces, derive from those and export global factory functions from your DLL. That way, you can change implementation details of a class. But you can't derive from that class in another DLL. And changing the interface also breaks binary compatibility, of course.
有一些方法可以解决这个问题:例如,您可以定义公共抽象接口,从这些接口派生并从您的 DLL 导出全局工厂函数。这样,您就可以更改类的实现细节。但是您不能在另一个 DLL 中从该类派生。当然,更改接口也会破坏二进制兼容性。
I'm not sure if there is a good solution for this in native code: If the compiler/linker creates native code at compile time, then it must know the exact memory layout of the classes/structures that are used in code. If the last compilation step (generating native code) is delayed until a method is called for the first time, this problem simply goes away: you can modify a class in an assembly, and as long as the JIT can resolve all the used members at runtime, everything will run fine.
我不确定在本机代码中是否有一个很好的解决方案:如果编译器/链接器在编译时创建本机代码,那么它必须知道代码中使用的类/结构的确切内存布局。如果最后的统计处理(生成本地代码)被延迟,直到一个方法被调用的第一次,这个问题简单地消失了:你可以在修改类组件,并只要JIT可以解决所有使用的成员在运行时,一切都会正常运行。
In a nutshell: If you create a monolithic single-executable program, you could probably have most of the powerful features of .NET with a compiler that creates native code. But the disadvantages of having a JIT compiler (framework installation, slightly longer startup times) really don't outweigh the benefits in most cases.
简而言之:如果您创建一个单一的单一可执行程序,您可能会通过创建本机代码的编译器获得 .NET 的大部分强大功能。但在大多数情况下,使用 JIT 编译器的缺点(框架安装、启动时间稍长)确实并没有超过好处。

