所以,例如Python和Java有一个虚拟机,C和Haskell没有。(如果我错了,请纠正我)
考虑一下这条线两侧的语言,我找不到原因。Java在很多方面都是静态的,而Haskell提供了许多动态特性。
所以,例如Python和Java有一个虚拟机,C和Haskell没有。(如果我错了,请纠正我)
考虑一下这条线两侧的语言,我找不到原因。Java在很多方面都是静态的,而Haskell提供了许多动态特性。
这与静态 vs 动态无关。
相反,它是关于独立于底层硬件平台的 ("一次构建,到处运行" - 理论上...)
事实上,这也与语言无关。一个人可以编写生成 JVM 字节码的 C 编译器。一个人可以编写生成 x86 机器代码的 Java 编译器。
在我们暂且不考虑虚拟机(我们稍后会回到这个问题上,我保证),让我们先看一下一个重要的事实:
为了提供垃圾回收的功能,语言必须有某种"运行时"/运行环境/东西来执行它。
这就是为什么Python、Java和Haskell需要一个"运行时",而C不需要,可以直接编译成本地代码。
请注意,Psyco是一个将Python代码编译为机器码的优化器,但其中许多机器码都包含对C-Python运行时函数的调用,例如PyImport_AddModule
,PyImport_GetModuleDict
等。
Haskell/GHC和psyco编译的Python类似。整数(Int)被添加为简单的机器指令,但分配对象等更复杂的内容会调用运行时。
还有什么?
如果我们要在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'),
]
}
现在,让我们编写一个解释器来执行这个内存数据结构。
让我们讨论一下它相对于直接从文本解释器的好处,然后再讨论与编译为机器代码的好处。
wt(f, d(o, e), s) <= th(i, s) + cr(a, p * d + o)
VM(虚拟机)实际上是一种语言设计者避免编写语言实现复杂性的工具。
基本上,它是一个虚拟计算机规范,规定了该计算机的每个部件如何相互交互。您可以在此规范中编写一些假设,这些假设可以由实际语言使用或不使用。
在此规范中,通常定义处理器/处理器的工作方式、内存的工作方式、可行的读/写障碍等以及与其交互的较简单的汇编语言。
最终语言通常是从您编写的文本文件转换(编译)为针对该机器编写的表示形式。
这有一些优点:
还有很酷的因素:看吧,我制作了一个虚拟机 :)
没有“必要”,这些语言都提供编译器,直接发出机器代码来实现其语言在给定架构中的语义。
虚拟机的想法是为了抽象掉所有不同硬件和软件制造商之间的架构差异,以便开发人员有一个统一的机器可以编写。
Java和Python可以以维护平台无关性的方式进行编译。即使对于C#也是如此。优点在于虚拟机能够将这些大多数强类型字节码转换为非常好的平台特定代码,并具有相对较低的开销。由于Java旨在“一次构建 - 任何地方运行”,因此创建了JVM。