什么是虚拟机,动态语言为何需要它?
比如说,Python和Java有一个虚拟机,而C和Haskell就没有。(如果我说错了,请纠正我)
我在想这些语言之间的区别时,发现找不到具体的原因。Java在很多方面是静态的,而Haskell则提供了很多动态的特性。
8 个回答
虚拟机基本上就是一个解释器,它负责将一种接近机器代码的语言进行解释。当真实的机器解释真实的机器代码时,虚拟机则解释一种虚构的机器代码。有些虚拟机会解释实际计算机的机器代码,这种虚拟机被称为模拟器。
写一个简单的类似汇编语言的解释器比写一个完整的高级语言解释器要简单得多。此外,很多高级语言的代码结构其实只是一些基本原则的语法糖。因此,直接写一个将这些复杂概念翻译成简单虚拟机语言的编译器会更容易,这样我们就不需要写一个复杂的解释器,而是可以用一个简单的虚拟机来代替。这样一来,我们就能有更多时间来优化虚拟机。
这基本上就是现在大多数语言(不直接编译成真实机器代码的语言)实现的方式。
解释器(虚拟机)和编译器可以是两个独立的程序(比如 java
和 javac
),也可以是一个程序(像 Ruby 或 Python)。
这和静态和动态没有关系。
其实,这主要是为了让程序不依赖于特定的硬件平台(理论上讲就是“编写一次,处处运行”)。
实际上,这也和编程语言没有关系。你可以写一个C语言的编译器,让它生成JVM能理解的字节码。你也可以写一个Java编译器,让它生成x86机器码。
先暂时不谈虚拟机(VM),我们先来看看一个重要的事实:
C语言没有垃圾回收。
要让一种语言提供垃圾回收功能,就需要有某种“运行时环境”来执行这个功能。
这就是为什么Python、Java和Haskell需要一个“运行时”,而C语言不需要,所以它可以直接编译成本地代码。
值得注意的是,psyco是一个Python优化器,它将Python代码编译成机器代码,但很多机器代码实际上是调用C-Python运行时的函数,比如PyImport_AddModule
、PyImport_GetModuleDict
等。
Haskell/GHC的情况和psyco编译的Python类似。Int
类型被添加为简单的机器指令,但更复杂的操作,比如分配对象等,则需要调用运行时。
还有什么呢?
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'),
]
}
现在,让我们写一个解释器来执行这个内存中的数据结构。
接下来,我们来讨论这种方法相比直接从文本解释的好处,以及相比编译成机器代码的好处。
虚拟机相比直接从文本解释的好处
- 虚拟机系统在执行代码之前会给你所有的语法错误提示。
- 在评估一个循环时,虚拟机系统不会每次运行时都解析源代码。
- 这使得虚拟机比直接从文本解释器更快。
- 所以,直接解释器在处理长变量名时会运行得比较慢,而短变量名时会快。这会鼓励人们写一些糟糕的数学风格代码,比如
wt(f, d(o, e), s) <= th(i, s) + cr(a, p * d + o)
虚拟机相比编译成机器代码的好处
- 描述程序的内存数据结构,或者说“虚拟机代码”,可能比那些重复做同样事情的冗长机器代码要紧凑得多。这会让虚拟机系统运行得更快,因为需要从内存中获取的“指令”会更少。
- 创建一个虚拟机比创建一个机器代码编译器简单得多。你可能现在就能做到,而不需要了解任何汇编语言或机器代码。