CLR是虚拟机吗?

我读过一本将.net CLR称为虚拟机的书吗? 任何人都可以certificate这一点? 我们在某些开发平台上需要虚拟机的概念是什么原因?

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

CLR作为虚拟机的书是“ Professional .Net Framework 2.0 ”。

这里有很多误解。 如果你真的想要的话,我想你可以把.Net当成虚拟机,但是让我们来看看.Net Framework如何处理你的代码。 典型的情况是这样的

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

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

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

我想你可以把它称为一个虚拟机,从这个意义上说,JITter从开发者中抽象出真正的机器的细节。 就我个人而言,我不认为这是正确的,因为对于很多人来说,虚拟机意味着远离原生代码的运行时抽象,而.NET程序根本不存在。

关于整个过程的另一个关键点,是将其与“虚拟机”环境区分开来,这只是典型的过程。 如果你真的想要的话,你可以在发布之前预编译一个.Net程序集,并直接将本地代码部署到最终用户(提示:在程序的整个生命周期中聚合速度较慢,因为你失去了特定于计算机的优化)。 当然,你仍然需要安装.Net运行库,但是在这一点上,它和其他运行时API没什么区别。 它更像是一个集合dll,带有一个很好的API,你可以使用VB或C运行时,Microsoft也随Visual Studio一起提供。 这种情况使得IL脱离了图像,使虚拟机绰号难以certificate。 (我说“种”,因为IL仍然被部署并用于validation保存的代码,但是它从来没有被触及执行)。

还有一个要说的是缺less虚拟机进程。 运行应用程序时,没有运行通用的“沙箱”进程。 将其与Java进行比较,如果在程序运行时打开任务pipe理器,则会看到一个专门用于Java VM的进程,而应用程序的实际进程是VM创build的沙箱内的一个线程。 在.Net中,您直接在Windows任务pipe理器中看到应用程序的进程。

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

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

JVM解释包含java字节代码的程序,.net CLR解释包含Microsoft称为“中间语言(IL)”指令的程序。 这些字节码之间有区别,但是虚拟机是相似的并且渴望提供相似的特征。

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

但是,JIT代码与本机代码在质量和数量上是不同的,如下所述。 因此,本文将JIT代码视为运行特定字节码程序时的虚拟机本地实现

这些虚拟机(VM)渴望提供的一个特征是以防止某些危险编程错误的forms的安全性。 例如,这个网站论坛的标题,stackoverflow,是由本地代码中可能出现的这种types的危险错误的启发。

为了提供安全性和执行安全性,虚拟机在“虚拟机级”实现types安全。 需要分配到虚拟机内存来存储保存在该内存位置的数据types。 例如,如果一个整数被压入栈中,就不可能从栈中popup一个double。 C式“工会”是被禁止的。 指针和直接访问内存是被禁止的。

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

对于虚拟机,types安全是在程序员允许访问的“最低级别”执行的。 (忽略了可以编写托pipe本机代码的那一刻,也就是说)。因此,没有用户会遇到执行需要直接访问存储器位置和指针的危险操作之一的应用程序。

实际上,.net CLR实现了一种编写本地代码的方式,可以通过.net“托pipe”代码调用。 在这种情况下,本地代码作者的负担就是不要犯任何指针和内存错误。

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

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

当然,你可以编写一个.NET编译器,以便编译成CPU汇编语言而不是IL。 但是,这对于所有的CPU都是不可移植的 – 你必须为每个OS / CPU对编译一个不同的版本。 但通过编译为ILM,您可以让“虚拟机”处理CPU和操作系统特定的东西。

我有点老派,所以我也把CLR称为虚拟机器。 我的推理是,CLR从一个中间字节码中组装机器码,这也是虚拟机的function。

CLR的好处主要是由于它组装使用运行时types信息的机器代码。

您可以使用本机types开发一个与.NET框架一样强大的本地框架。 您失去的唯一灵活性是如果您将程序传输到另一个平台而无需重新编译,则可以重新组合本机代码。

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

JVM和CLR都不会做任何与其他语言的大多数“虚拟机”所做的任何重大不同的事情。 现代的他们都使用JIT来将虚拟指令(p代码,字节代码,中间语言指令,任何你喜欢的称之为)转换为“本地CPU硬件”指令(“机器代码”)。

实际上,第一台“虚拟机”就是Smalltalk虚拟机。 这个创新的作者Peter Deutsch将其称为“dynamic翻译”,而不是被Java推广的术语“JIT”。 如果Smalltalk的“运行时执行环境”将被称为“虚拟机”(这就是所谓的“什么”),那么任何和所有其他“运行时系统”基本上都是相同的东西,也就是“虚拟机”。 “

你有很多有价值的答案,但我认为有一件事还没有提到:模块化。

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

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

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

简而言之:如果您创build了一个单一的单个可执行程序,那么您可能拥有.NET的大部分强大function,并带有编译器来创build本机代码。 但是,在大多数情况下,使用JIT编译器(框架安装,启动时间稍长一些)的缺点实际上并没有超过其好处。