24

因此,例如,Python 和 Java 有一个 VM,而 C 和 Haskell 则没有。(如我错了请纠正我)

想着线路两边都有哪些语言,找不到原因。Java 在很多方面都是静态的,而 Haskell 提供了很多动态特性。

4

8 回答 8

28

这与静态与动态无关。

相反,它是关于独立于底层硬件平台(“构建一次,到处运行” - 理论上......)

实际上,这也与语言无关。可以编写一个为 JVM 生成字节码的 C 编译器。可以编写一个生成 x86 机器代码的 Java 编译器。

于 2011-01-09T18:08:07.450 回答
22

让我们暂时忘记虚拟机(我保证,我们会回到下面的那些),并从这个重要的事实开始:

C没有垃圾收集。

对于提供垃圾收集的语言,必须有某种“运行时” /运行时环境/事物来执行它。

这就是为什么 Python、Java 和 Haskell 需要“运行时”,而 C 不需要,可以直接编译为本机代码。

请注意,psyco是一个 Python 优化器,可以将 Python 代码编译为机器代码,但是,许多机器代码包含对 C-Python 运行时函数的调用,例如PyImport_AddModulePyImport_GetModuleDict等。

Haskell/GHC 与 psyco 编译的 Python 类似。Ints 作为简单的机器指令添加,但分配对象等更复杂的东西会调用运行时。

还有什么?

C没有“例外”

如果我们要向 C 添加异常,我们生成的机器代码将需要为每个函数和每个函数调用做一些事情。

如果我们再添加“闭包”,就会添加更多的东西。

现在,不是在每个函数中重复这个样板机器代码,我们可以让它调用一个子过程来做必要的事情,比如PyErr_Occurred.

所以现在,基本上每个原始源代码行都映射到对某些函数的一些调用和一个较小的独特部分。

但是,只要我们为每个原始源代码行做这么多事情,为什么还要为机器代码而烦恼呢?

这是一个想法(顺便说一句,我们称这个想法为“虚拟机”)。

让我们代表您的 Python 代码,例如:

def has_no_letters(text):
  return text.upper() == text.lower()

作为内存数据结构,例如:

{ 'func_name': 'has_no_letters',
  'num_args': 1,
  'kwargs': [],
  'codez': [
    ('get_attr', 'tmp_a', 'arg_0', 'upper'),  # tmp_a = arg_0.upper
    ('func_call', 'tmp_b', 'tmp_a', []),  # tmp_b = tmp_a() # tmp_b = arg_0.upper()
    ('get_attr', 'tmp_c', 'arg_0', 'lower'),
    ('func_call', 'tmp_d', 'tmp_c', []),
    ('get_global', 'tmp_e', '=='),
    ('func_call', 'tmp_f', 'tmp_e', ['tmp_b', 'tmp_d']),
    ('return', 'tmp_f'),
  ]
}

现在,让我们编写一个解释器来执行这个内存数据结构。

让我们讨论一下直接来自文本解释器的好处,然后讨论编译成机器代码的好处。

虚拟机相对于直接来自文本的解释器的优势

  • VM 系统在执行代码之前会为您提供所有语法错误。
  • 在评估循环时,VM 系统不会在每次运行时解析源代码。
    • 使 VM 比直接来自文本的解释器更快。
    • 因此,直接解释器在长变量名时运行速度较慢,而在短变量名时运行速度更快。这鼓励人们编写蹩脚的数学家风格的代码,例如wt(f, d(o, e), s) <= th(i, s) + cr(a, p * d + o)

与编译成机器码相比,VM 的优势

  • 描述程序的内存数据结构或“VM 代码”可能会比样板完整的机器代码更紧凑,后者对每一行原始代码一次又一次地执行相同的操作。这将使 VM 系统运行得更快,因为需要从内存中获取的“指令”更少。
  • 创建 VM 比为机器代码创建编译器要简单得多。您现在甚至可以在不知道任何汇编/机器代码的情况下执行此操作。
于 2011-01-10T23:28:55.800 回答
4

VM(虚拟机)实际上是语言设计人员在编写语言实现时避免一些复杂性的工具。

基本上是虚拟计算机的规范,以及所述计算机的每一部分将如何与另一部分交互。您可以在本规范中编写一些可以被实际语言使用或不使用的假设。

在本规范中,您通常定义处理器/处理器的工作方式、内存的工作方式、可能的读/写障碍等,以及与之交互的更简单的汇编语言。

最终语言通常从您正在编写的文本文件中翻译(编译)为为该机器编写的表示形式。

这有一些优点:

  • 您将语言与特定的硬件架构分离
  • 通常允许你控制发生的事情
  • 不同的人可以移植到不同的架构
  • 您有更多信息可以优化代码
  • 等等

还有酷的因素:看马我做了一个虚拟机:)。

于 2011-01-09T18:16:36.970 回答
4

虚拟机基本上是一种解释器,可以解释更接近机器代码的语言。当真实机器解释真实机器代码时,虚拟机解释一个虚构的机器代码。一些 VM-s 解释实际计算机的机器代码——这些被称为仿真器。

为简单的类汇编语言编写解释器更容易,然后为完整的高级语言编写解释器。此外,许多高级代码结构通常只是一些基本原则的语法糖。因此,编写一个将所有这些复杂概念转换为简单 VM 语言的编译器会更容易,因此我们不必编写复杂的解释器,但可以使用简单的解释器(VM)。然后你有更多的时间来优化虚拟机。

这基本上就是当今大多数语言(不编译成真正的机器代码)的实现方式。

解释器 (VM) 和编译器可以是单独的程序(如javajavac),也可以只是一个程序(如 Ruby 或 Python)。

于 2011-01-09T19:52:17.783 回答
3

虚拟机上的维基百科条目:

“虚拟机(VM)是机器(即计算机)的软件实现,它像物理机器一样执行程序。”

理论上,虚拟机最大的资产是代码可移植性—— “一次编写,随处运行”

虚拟机最著名的例子可能是JVM,最初设计用于运行 Java 代码,但现在也越来越多地用于 Clojure 和 Scala 等语言。

没有什么特定于动态语言的,这意味着它们需要一个虚拟机。然而,他们确实需要一个解释器,它可以构建在虚拟机上。

于 2011-01-09T18:12:27.043 回答
2

没有“需要”,这些语言中的任何一种都提供了直接发出机器代码的编译器,以在给定的架构中实现其语言的语义。

虚拟机的想法是抽象出所有不同硬件和软件制造商之间的架构差异,以便开发人员只有一台机器可以写入。

于 2011-01-09T18:10:02.817 回答
2

Java 和 python 可以以保持平台独立性的方式进行编译。这甚至适用于 C#。优点是 VM 能够将这种主要是强类型字节码转换为非常好的平台特定代码,并且开销相对较低。由于 Java 旨在“构建一次 - 随处运行”,因此创建了 JVM。

于 2011-01-09T18:10:30.610 回答
2

想象一下,您创建了一种编程语言:您弄清楚了语言语义并开发了一种很好的语法。

但是,文本表示是不够的:在执行程序时必须一次又一次地解析文本效率低下,因此添加内存中的二进制表示是很自然的。将其与自定义内存管理器相结合,您基本上就拥有了一个虚拟机。

现在,为了额外的点,要么开发一个字节码格式来序列化你的内存表示和一个运行时加载器,或者,如果你想走脚本语言的道路,一个eval()函数。

对于大结局,添加 JIT。

于 2011-01-09T21:58:35.720 回答