CLR是虚拟机吗?

发布于 2024-08-07 04:30:24 字数 191 浏览 7 评论 0原文

我读过一本书,其中提到 .net CLR 作为虚拟机?任何人都可以证明这一点吗?为什么我们在某些开发平台上需要虚拟机的概念?

是否有可能开发一个完全面向对象且像.net一样强大的本机框架(没有虚拟机的框架)?

将CLR称为虚拟机的书是《Professional .Net Framework 2.0》。

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?

Isn't it possible to develop a native framework [one without virtual machine] that is fully object oriented and as powerful as .net?

The book which refers to CLR as virtual machine is "Professional .Net Framework 2.0".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

泅渡 2024-08-14 04:30:24

这里存在很多误解。我想如果您确实愿意,您可以将 .Net 视为虚拟机,但让我们看看 .Net Framework 如何真正处理您的代码。典型的场景如下所示:

  1. 您用 C#、VB.Net、F# 或其他兼容语言编写 .Net 程序。
  2. 该代码被编译为中间语言 (IL),类似于 Java 的字节码,并分发到最终用户计算机。
  3. 最终用户在安装了正确版本的 .Net 的计算机上首次调用该程序
  4. 计算机认为这是一个 .Net 程序集而不是“原始”机器代码,并将其传递给 JIT 编译器
  5. JIT 编译器 < em>将 IL 编译为完全本机的机器代码。
  6. 本机代码在程序执行期间保存在内存中。
  7. 调用保存的本机代码,IL 不再重要。

这里有几个重要的点,但最重要的一点是,任何时候都不会解释任何代码。相反,您可以在步骤 5 中看到它被编译为本机代码。这与将其加载到虚拟机中相比存在巨大差异,原因如下:

  1. 完全编译的代码由 CPU 直接执行,而不是由额外的软件抽象层解释或翻译,这应该更快。
  2. JIT 编译器可以利用特定于运行程序的单个机器的优化,而不是满足于最低公分母。
  3. 如果您愿意,您甚至可以预编译代码,本质上对用户完全隐藏步骤 5。

我想您可以将其称为虚拟机,从某种意义上说,JITter 从开发人员那里抽象出了真实机器的细节。就我个人而言,我认为这并不正确,因为对于很多人来说,虚拟机意味着一种远离本机代码的运行时抽象,而对于 .Net 程序来说,这种抽象并不存在。

关于这一点的另一个关键点整个过程真正区别于“虚拟机”环境的地方在于它只是典型过程。如果您确实愿意,您可以在分发之前预编译 .Net 程序集,并将本机代码直接部署给最终用户(提示:在程序的生命周期中,总体速度会更慢,因为您会失去特定于机器的优化)。当然,您仍然需要安装 .Net 运行时,但此时它实际上与任何其他运行时 API 没有太大区别;它更像是一个带有很好的 API 的 dll 集合,您可以链接到该 API,就像您可以使用 VB 或 C 运行时一样,Microsoft 也随 Visual Studio 一起提供了这些 API。这种方式将 IL 排除在外,使得 VM 的绰号更难证明其合理性。 (我说“有点”是因为 IL 仍然被部署并用于验证保存的代码,但它本身从未被触及来执行)。

另一点是缺乏虚拟机进程。当您运行应用程序时,不会运行常见的“沙箱”进程。与Java相比,如果在程序运行时打开任务管理器,您将看到专门针对Java VM的进程,而应用程序的实际进程是VM创建的沙箱内的线程。在.Net 中,您可以直接在Windows 任务管理器中看到应用程序的进程。

总之:您可以说 IL + CLR + JIT 一起以某种方式构成了虚拟机。我个人不这么认为,但如果你相信这一点,我不会与你争论。我想说的一点是,当你告诉某人 .Net 在虚拟机中运行而无需进一步解释时,你与该人传达的想法是“主机进程中解释的字节码”。那是错误的。


更新 这个答案现在有点旧了,事情已经发生了变化,使得 .Net 不再像虚拟机。在容器时代,冷启动时间可能更重要,我的理解是最新版本的 .Net Core 有更多工具可以使部署本机代码(并在每次启动时跳过 JIT 步骤)变得更加容易。

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

  1. You write a .Net program in C#, VB.Net, F#, or some other compatible language.
  2. That code is compiled down to an Intermediate Language (IL), which is similar to Java's bytecode, that is distributed to end-user machines.
  3. An end user invokes the program for the first time on a computer with the right version of .Net installed
  4. The computer sees this is a .Net assembly rather than "raw" machine code, and passes it off to the JIT compiler
  5. The JIT compiler compiles the IL to fully-native machine code.
  6. The native code is saved in memory for the life of this program execution.
  7. The saved native code is invoked, and the IL no longer matters.

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 huge difference than loading it into a virtual machine, for several reasons:

  1. 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.
  2. The JIT compiler can take advantage of optimizations specific to the individual machine running the program, rather than settling for a lowest common denominator.
  3. If you want you can even pre-compile the code and in essence hide step 5 from the user completely.

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.

One other key point about this whole process that really sets it apart from a "virtual machine" environment is that it's only the typical process. 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).

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.

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.


Update This answer is kind of old now, and things have changed to make .Net even less like a virtual machine. In the era of containers, cold start times can mattter a lot more, and my understanding is recent versions of .Net Core have more tools to make deploying native code — and skipping the JIT step on each startup — even easier.

空城之時有危險 2024-08-14 04:30:24

与 Java 虚拟机 (JVM) 类似,.net CLR 是字节码解释虚拟机。

JVM 解释包含 java 字节代码的程序,.net CLR 解释包含 Microsoft 所谓的“中间语言 (IL)”指令的程序。这些字节码之间存在差异,但虚拟机是相似的并且渴望提供相似的功能。

这两种虚拟机实现都能够将其输入字节码编译为它们所运行的计算机的机器语言。这称为“即时编译 (JIT)”,生成的输出代码称为“JIT 代码”。由于 JIT 代码包含计算机 CPU 机器语言的指令序列,因此该代码有时称为“本机”代码。

然而,JIT 代码在质量和数量上都与本机代码不同,如下所述。因此,本文认为 JIT 代码只不过是运行特定字节码程序时虚拟机的本机实现。

这两种虚拟机 (VM) 渴望提供的一项功能是防止某些危险编程错误的安全性。例如,该网站论坛的标题 stackoverflow 的灵感来自于本机代码中可能出现的一种危险错误。

为了提供安全性和执行安全性,VM 在“虚拟机级别”实现类型安全。需要分配给 VM 内存来存储该内存位置中保存的数据类型。例如,如果将整数压入堆栈,则无法从堆栈中弹出双精度数。禁止 C 风格的“联合”。禁止指针和直接访问内存。

如果结果是本机二进制文件(例如 EXE 文件),那么通过向开发人员强制实施面向对象的语言框架,我们就无法获得相同的好处。在这种情况下,我们将无法区分使用框架生成的本机二进制文件和恶意用户使用框架以外的源生成的 EXE。

对于虚拟机,类型安全是在允许程序员访问的“最低级别”强制执行的。 (暂时忽略可以编写托管本机代码。)因此,没有用户会遇到执行需要直接访问内存位置和指针的危险操作之一的应用程序。

实际上,.net CLR 实现了一种编写可由 .net“托管”代码调用的本机代码的方法。在这种情况下,本机代码作者有责任不犯任何指针和内存错误。

由于 JVM 和 .net CLR 都执行 JIT 编译,因此任一 VM 实际上都会根据提供的字节码创建本机编译的二进制文件。此“JIT 代码”的执行速度比 VM 的解释器执行速度更快,因为即使 JIT 生成的机器语言代码也包含 VM 将执行的所有 VM 所需的安全检查。因此,JIT 输出代码不如本机代码快,本机代码通常不包含大量运行时检查。然而,这种速度性能的缺陷却换来了包括安全性在内的可靠性的提高;特别是,防止使用未初始化的存储,强制执行分配的类型安全,执行范围检查(从而防止基于堆栈和堆的缓冲区溢出),对象生命周期由垃圾收集管理,动态分配是类型安全的。执行此类运行时行为检查的环境正在实现虚拟机的规范,并且只不过是虚拟机的机器语言实现。

Similar to the Java Virtual Machine (JVM), the .net CLR is a byte-code interpreting virtual machine.

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.

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.

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.

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.

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.

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.

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.

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.

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.

夏雨凉 2024-08-14 04:30:24

“虚拟机”部分是指 .NET 代码被编译为 EXE 和 DLL 作为“中间”汇编语言 (IL) 在虚拟机上运行,​​而不是真正的 CPU 汇编语言。然后,在运行时,ILM 被转换为真正的 CPU 程序集以供执行(称为即时编译或 JIT 编译)。

当然,您可以编写一个 .NET 编译器,以便将其编译为 CPU 汇编语言而不是 IL。但是,这无法移植到所有 CPU - 您必须为每个操作系统/CPU 对编译不同的版本。但通过编译为 ILM,您可以让“虚拟机”处理 CPU 和操作系统特定的内容。

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).

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.

泪眸﹌ 2024-08-14 04:30:24

JVM 和 CLR 所做的事情都与其他语言的大多数“虚拟机”所做的事情没有本质上的不同。现代的,它们都使用 JIT 将虚拟指令(p 代码、字节码、中间语言指令,随便你怎么称呼它)转换为“本机 CPU 硬件”指令(“机器代码”)。

事实上,第一个“虚拟机”做到这一点的是 Smalltalk 虚拟机。这项创新的作者 Peter Deutsch 将其称为“动态翻译”,而不是由 Java 普及的术语“JIT”。如果 Smalltalk“运行时执行环境”将被称为“虚拟机”(现在仍然如此),那么任何和所有其他执行基本相同操作的“运行时系统”也有资格称为“虚拟机”。 ”

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.")

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."

何以心动 2024-08-14 04:30:24

我有点守旧,所以我也将 CLR 称为虚拟机。我的理由是,CLR 从中间字节码组装机器代码,这也是虚拟机所做的事情。

CLR 的优势主要在于它利用运行时类型信息来汇编机器代码的方式。

您可以仅使用本机类型来开发与 .NET 框架一样强大的本机框架。如果您将程序传输到另一个平台而不重新编译,那么您失去的唯一灵活性是重新组装本机代码的能力。

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.

The benefits of the CLR is mainly due to the way it assembles the machine code which utilizes runtime type information.

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.

毁虫ゝ 2024-08-14 04:30:24

CLR 的优点是可以自由地用开发人员选择的任何编程语言编写代码,因为代码在解释为本机调用之前将被编译为 CLR。 .NET 框架使用这种 JIT 编译来统一处理所有内容,并输出适用于所部署平台的程序,这是编译语言所不具备的。

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.

十级心震 2024-08-14 04:30:24

您已经得到了很多有价值的答案,但我认为有一件事还没有被提及:模块化。

从本机 DLL 导出 OO 类非常困难。当然,您可以告诉链接器导出该类并将其导入到其他地方,但这很脆弱;更改类中的单个私有成员将破坏二进制兼容性,即,如果您更改一个 DLL 而不重新编译所有其他模块,您的程序将在运行时严重崩溃。

有一些方法可以解决这个问题:例如,您可以定义公共抽象接口,从这些接口派生并从 DLL 导出全局工厂函数。这样,您就可以更改类的实现细节。但是您不能从另一个 DLL 中的该类派生。当然,更改接口也会破坏二进制兼容性。

我不确定本机代码中是否有一个好的解决方案:如果编译器/链接器在编译时创建本机代码,那么它必须知道代码中使用的类/结构的确切内存布局。如果最后一个编译步骤(生成本机代码)延迟到第一次调用方法时,这个问题就会消失:您可以修改程序集中的类,并且只要 JIT 可以解析所有使用的成员 < em>在运行时,一切都会运行良好。

简而言之:如果您创建一个整体的单一可执行程序,您可能可以通过创建本机代码的编译器获得 .NET 的大部分强大功能。但在大多数情况下,使用 JIT 编译器的缺点(框架安装、启动时间稍长)实际上并没有超过其优点。

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.

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文