我正在阅读this question以找出Java虚拟机和.NET CLR之间的差异,Benji的回答让我想知道为什么首先需要虚拟机。
根据我对Benji解释的理解,虚拟机的JIT编译器将中间代码解释为在CPU上运行的实际汇编代码。它必须这样做的原因是因为CPU通常具有不同数量的寄存器,并且根据Benji的说法,“一些寄存器是特殊用途的,并且每个指令都要求其操作数在不同的寄存器中。”这是有道理的,因此需要像虚拟机这样的中间解释器,以便可以在任何CPU上运行相同的代码。
但是,如果是这种情况,那么我不明白为什么编译成机器代码的C或C ++代码能够在任何计算机上运行,只要它是正确的操作系统。为什么我使用Pentium在我的Windows机器上编译的C程序能够在使用AMD的其他Windows机器上运行?
如果C代码可以在任何CPU上运行,那么虚拟机的目的是什么?是否可以在任何操作系统上运行相同的代码?我知道Java在几乎任何操作系统上都有VM版本,但除了Windows之外还有其他操作系统的CLR吗?
或者还有什么我想念的?操作系统是否对其运行的汇编代码进行了其他解释,以使其适应特定的CPU或其他内容?
我很好奇这一切是如何运作的,所以非常感谢明确的解释。
注意:我之所以不在JVM与CLR问题中发表我的查询作为评论的原因是因为我没有足够的积分来发表评论= b。
编辑:感谢所有精彩的答案!所以我似乎缺少的是,虽然所有处理器都有差异,但是有一个共同的标准化,主要是X86架构,它提供了足够大的通用功能集,因此在一个X86处理器上编译的C代码将在大多数情况下工作在另一个X86处理器上。这进一步推动了虚拟机的正当性,更不用说我忘记了垃圾收集的重要性了。
答案 0 :(得分:38)
AMD和英特尔处理器使用相同的指令集和机器架构(从执行机器代码的角度来看)。
C和C ++编译器编译为机器代码,其头部适合他们所针对的操作系统。一旦编译,它们就不再以任何方式,形状或形式与它们编译的语言相关联,并且仅仅是二进制可执行文件。 (有些文物可能会显示它是从哪种语言编译而来的,但这不是重点)
因此,一旦编译,它们就与机器(X86,intel和amd指令集和体系结构)和操作系统相关联。
这就是为什么他们可以在任何兼容的x86机器和任何兼容的操作系统(win95到winvista,某些软件)上运行。
但是,它们无法在OSX计算机上运行,即使它在intel处理器上运行 - 除非您运行其他仿真软件(例如parallels或带有Windows的VM),否则二进制文件不兼容。
除此之外,如果你想在ARM处理器,MIPS或PowerPC上运行它们,那么你必须运行一个完整的机器指令集模拟器,它将X86中的二进制机器代码解释为你正在运行它的任何机器上。
与.NET对比。
.NET虚拟机的制作好像世界上有更好的处理器 - 理解对象,内存分配和垃圾收集的处理器,以及其他高级构造。它是一台非常复杂的机器,现在无法直接在硅片中构建(具有良好的性能),但可以编写一个仿真器,允许它在任何现有的处理器上运行。
突然之间,您可以为要运行.NET的任何处理器编写一个特定于计算机的模拟器,然后可以在其上运行任何.NET程序。无需担心操作系统或底层CPU架构 - 如果有.NET VM,则软件将运行。
但是让我们再深入一点 - 一旦你掌握了这种通用语言,为什么不让编译器将任何其他书面语言转换成它呢?
现在你可以使用C,C#,C ++,Java,javascript,Basic,python,lua或任何其他语言编译器来转换已编写的代码,以便它可以在这个虚拟机上运行。
你已经将机器与语言分离了2度,并且没有太多的工作,你可以让任何人编写任何代码并让它在任何机器上运行,只要存在编译器和VM来映射这两个分离度。
如果您仍然想知道为什么这是一件好事,请考虑早期的DOS机器,以及Microsoft对该世界真正的贡献:
Autocad必须为每个可以打印的打印机编写驱动程序。莲花1-2-3也是如此。事实上,如果您想要打印软件,则必须编写自己的驱动程序。如果有10个打印机和10个程序,则必须单独和独立地编写100个不同的基本相同的代码。
Windows 3.1试图完成的(与GEM一起,以及许多其他抽象层)使打印机制造商为其打印机编写了一个驱动程序,程序员为Windows打印机类编写了一个驱动程序。
现在有10个程序和10个打印机,只需编写20个代码,并且由于代码的microsoft方面对每个人来说都是相同的,因此来自MS的示例意味着您几乎没有工作要做。 / p>
现在,一个程序并不仅限于他们选择支持的10台打印机,而是制造商为Windows提供驱动程序的所有打印机。
在应用程序开发中也出现了同样的问题。由于我不使用MAC,因此我无法使用真正的应用程序。有大量重复(我们真正需要多少世界级文字处理器?)。
Java本来是为了解决这个问题,但它有许多限制,其中一些并没有真正解决。
.NET更接近,但没有人为Windows以外的平台开发世界级的虚拟机(单声道是如此接近......但还不完全存在)。
所以......这就是我们需要虚拟机的原因。因为我不想仅仅因为他们选择了与我自己不同的操作系统/机器组合而将自己限制在较小的受众群体中。
- 亚当
答案 1 :(得分:7)
您认为C代码可以在任何处理器上运行是不正确的。像寄存器和字节序这样的东西会使编译的C程序在一个平台上根本不起作用,而它可能在另一个平台上起作用。
然而,处理器共享存在某些相似之处,例如,英特尔x86处理器和AMD处理器共享足够大的属性集,大多数代码编译的属性将在另一个上运行。但是,如果要使用特定于处理器的属性,则需要一个编译器或一组库来执行此操作。
至于为什么你想要一个虚拟机,除了它将为你处理处理器差异的声明之外,还有一个事实是虚拟机为C ++编译的程序无法使用的代码提供服务(不管理)今天。
提供的最突出的服务是垃圾收集,由CLR和JVM提供。这两个虚拟机都免费为您提供此服务。他们为你管理记忆。
还提供了边界检查,访问违规(尽管仍然可能,非常困难)等事情。
CLR还为您提供了一种代码安全形式。
这些都不是作为许多其他语言的基本运行时环境的一部分提供的,这些语言不能与虚拟机一起运行。
您可能通过使用库获得其中一些,但随后会强制您使用库的模式,而在.NET和Java服务中,通过CLR和JVM提供给您的访问是一致的。
答案 2 :(得分:4)
本质上,它允许“托管代码”,这意味着它所说的 - 虚拟机在运行时管理代码。这样做的三个主要好处是即时编译,托管指针/垃圾收集和安全控制。
对于即时编译,虚拟机监视代码执行,因此代码运行得更频繁,重新优化运行速度更快。您无法使用本机代码执行此操作。
托管指针也更容易优化,因为虚拟机会在它们四处走动时跟踪它们,并根据它们的大小和生命周期以不同的方式对它们进行管理。在C ++中很难做到这一点,因为你无法真正告诉指针只是在阅读代码的位置。
安全性是一个不言自明的安全性,虚拟机会阻止代码执行它不应该做的事情,因为它正在观察。我个人认为这可能是微软为C#选择托管代码的最大原因。
基本上我的观点是,因为虚拟机可以在发生代码时观察代码,它可以做一些事情,使程序员的生活更轻松,并使代码更快。
答案 3 :(得分:4)
大多数编译器,甚至是本机代码编译器,都使用某种中间语言。
这主要是为了降低编译器的构建成本。世界上有许多(N)编程语言。世界上还有许多(M)硬件平台。如果编译器在不使用中间语言的情况下工作,那么为支持所有硬件平台上的所有语言而需要编写的“编译器”总数将为N * M.
然而,通过定义一个中间语言并将编译器分为两部分,前端和后端,前端将源代码编译为IL,后端将IL编译为机器代码,您可以逃脱只写N + M编译器。这最终会节省大量成本。
CLR / JVM编译器和本机代码编译器之间的最大区别在于前端编译器和后端编译器之间的链接方式。在本机代码编译器中,这两个组件通常组合在同一个可执行文件中,当程序员在IDE中命中“build”时,它们都会运行。
使用CLR / JVM编译器,前端和后端在不同时间运行。前端在编译时运行,产生实际发送给客户的IL。然后,后端体现在一个在运行时调用的独立组件中。
因此,这提出了另一个问题,“延迟后端编译直到运行时有什么好处?”
答案是:“这取决于”。
通过将后端编译延迟到运行时,可以发送一组可以在多个硬件平台上运行的二进制文件。它还使程序可以利用后端编译技术的改进而无需重新部署。它还可以为有效实现许多动态语言功能提供基础。最后,它提供了在单独编译的动态链接库(dll)之间引入安全性和可靠性约束的能力,这是前期机器代码编译所无法实现的。
然而,也有缺点。实现广泛的编译器优化所需的分析可能很昂贵。这意味着“JIT”后端通常会比前端后端做更少的优化。这可能会影响性能。此外,在运行时调用编译器的需要也增加了加载程序所需的时间。使用“前期”编译器生成的程序没有这些问题。
答案 4 :(得分:3)
首先,机器代码不是cpu最低形式的指令。今天的x86 CPUS自己使用微代码将X86指令集解释为另一种内部格式。实际编程微码的唯一人员是芯片开发工程师类型,他们忠实而无痛地模拟传统的x86指令芯片,以使用当今的技术实现最大性能。
开发人员类型一直在添加额外的抽象层,因为它们带来了强大的功能。在所有更好的抽象之后,可以更快速,更可靠地编写新的应用程序。企业不关心他们的代码是什么或如何看起来他们只是希望可靠而快速地完成工作。如果应用程序的C版本减少几毫秒但最终花费两倍的时间来开发真的很重要吗?
速度问题几乎不是一个争论,因为为数百万人提供服务的许多企业应用程序都是用平台/语言编写的,例如java,例如GMail,GMaps。忘掉哪个语言/平台最快。更重要的是,您使用正确的算法并编写有效的代码并完成工作。
答案 5 :(得分:2)
答案 6 :(得分:2)
我知道Java在几乎任何操作系统上都有虚拟机版本,但除了Windows之外还有其他操作系统的CLR吗?
答案 7 :(得分:2)
以一种非常简单的方式,这是因为英特尔和AMD实现相同的汇编语言,具有相同数量的寄存器等等......
因此,您的C编译器会编译代码以在Linux上运行。该程序集使用Linux ABI,因此只要编译程序在Linux上运行,在x86程序集上运行,并且正确的函数签名,那么一切都很花哨。
现在尝试使用已编译的代码,并坚持使用,例如Linux / PPC(例如旧版iBook上的Linux)。那不行。作为Java程序的地方,因为JVM已经在Linux / PPC平台上实现。
现在的程序集langauge基本上是程序员可以编程的另一个接口。 x86(32位)允许您访问用于通用整数寄存器的eax,ebx,ecx,edx和用于浮点的f00-f07。在幕后,CPU实际上还有一百多个寄存器,并将这些东西混杂在一起以挤出性能。
答案 8 :(得分:0)
你的分析是正确的,java或C#可以设计为直接编译在任何机器上运行,如果他们这样做可能会更快。但虚拟机方法可以完全控制代码运行的环境,VM会创建一个安全的沙箱,只允许具有正确安全访问权限的命令执行可能具有破坏性的代码 - 例如更改密码或更新HD引导扇区。还有许多其他好处,但这是杀手锏的原因。你无法在C#中获得StackOverflow ...
答案 9 :(得分:0)
我认为你的问题的前提是有效的 - 你肯定不是第一个提出这个问题的人。请查看http://llvm.org以查看替代方法(现在正在运行的项目?或由Apple赞助)