为什么几乎所有的OO语言都编译为字节码?

时间:2010-10-17 21:25:45

标签: oop compiler-construction programming-languages bytecode

在我所知道的面向对象语言中,除了C ++和Objective-C之外几乎所有语言都编译为在某种虚拟机上运行的字节码。为什么有这么多不同的语言在编译成字节码时,而不是机器码?在princible中是否可以使用高级内存管理的OOP语言编译为机器代码?

编辑:我知道多平台支持通常是这种方法的优势。但是,很有可能在多个平台上进行本机编译,而无需为每个平台创建新的编译器。每个例子都可以发出C代码,然后用GCC编译它。

8 个答案:

答案 0 :(得分:13)

事实上没有理由,这是一种巧合。 OOP现在是“大”编程中的领先概念,因此虚拟机就是如此。

另请注意,传统虚拟机有两个不同的部分 - 垃圾收集器字节码解释器/ JIT编译器,这些部分可以单独存在。例如,名为SBCL的Common Lisp实现将程序编译为本机代码,但在运行时大量使用垃圾收集。

答案 1 :(得分:12)

这样做是为了让VM或JIT编译器有机会根据需要为执行代码的体系结构最佳地编译代码。此外,它允许创建跨平台字节码一次,然后在多个硬件架构上执行。这允许将特定于硬件的优化放入已编译的代码中。

由于字节代码不仅限于微体系结构,因此它可能比机器代码小。复杂指令可以表示为现代CPU中可用的更原始的指令,因为CPU指令设计中的约束与设计字节码体系结构的约束非常不同。

然后是安全问题。字节码可以在执行之前进行验证和分析(即,没有缓冲区溢出,某种类型的变量被访问为不存在的东西),等等......

答案 2 :(得分:4)

Java使用字节码,因为它的两个初始设计目标是可移植性和紧凑性。这两者都来自嵌入式设备语言的初始愿景,其中代码片段可以即时下载。

Python,Ruby,Smalltalk,javascript,awk等都使用字节码,因为编写本机编译器需要做很多工作,但是文本解释器太慢了 - 字节码打得很容易写,但也令人满意的快速运行。

我不知道为什么Microsoft语言使用字节码,因为对于它们来说,既不是可移植性也不是紧凑性是一个大问题。 CLR背后的许多想法来自剑桥的计算机科学家,因此我想到了易于程序分析和验证等考虑因素。

请注意,除了C ++和Objective C之外,Eiffel,Ada 9X,Vala和Go都是直接编译为本机代码的OO语言(不同年份)。

总而言之,我会说OO和字节码并不是齐头并进的。相反,我们有几个开发流程巧合的融合:Python和Ruby等脚本语言的传统字节编码解释器,Java的疯狂Gosling总体规划,以及微软的动机是什么。

答案 3 :(得分:3)

将大多数解释语言(不是特定的OO语言)编译为字节码的最大原因是为了提高性能。解释代码中最昂贵的部分是将文本源转换为中间表示。例如,执行以下操作:

foo + bar;

解释器必须扫描10个字符,将它们转换为4个标记,为操作构建AST,解析三个符号(+是符号,这取决于foo和bar的类型),所有这些都可以执行任何实际取决于程序运行时状态的操作。这些都不会在运行中发生变化,因此许多语言都试图存储某种形式的中间表示。

字节码,而不是存储AST有一些优点。例如,字节码很容易序列化,因此IR可以写入磁盘并在下次调用时重复使用,从而进一步缩短了解释时间。另一个原因是字节码通常占用较少的实际内存。显着的字节码表示通常很容易及时编译,因为它们通常在结构上与典型的机器代码相似。

答案 4 :(得分:1)

Bytecode比机器代码更灵活。首先,它提供了平台可移植性的基础,而无需编译器或运输源代码。因此,开发人员可以分发单个版本的应用程序,而无需放弃源代码,需要复杂的开发人员工具或预期潜在的目标平台。虽然后者并不总是实用,但确实会发生。特别是对于开发人员库说我分发了一个我只在Windows上测试过的库,但其他人在Linux或Android上使用它。它实际上经常发生,并且大部分时间它按预期工作。

字节代码通常也比解释器更优化,因为它更接近机器指令因此更快地转换为机器指令。并非所有OO语言都已编译。 Ruby, Python ,甚至Javascript都被解释为没有编译成任何东西所以ruby解释器必须采用非常灵活的语言并将其转换为指令,但这种灵活性需要付出代价。运行时:解析文本,生成AST,将AST转换为机器代码等。像JIT这样的优化也很容易,其中字节代码直接转换为机器代码,甚至可以为特定硬件创建优化。

最后,仅仅因为一种语言编译为字节码并不排除利用该字节代码的其他语言。现在,使用该字节代码的任何优化都可以应用于可能知道如何将自身转换为该字节代码的其他语言。这使得字节代码成为其他语言可重用性的一个非常重要的层。

OO和字节码编译可以追溯到70年代的Smalltalk,我相信有人会早在50年代/ 60年代就说LISP。但是,直到90年代它才真正开始大规模地用于生产系统。

本地编译听起来像是最佳路径,也可能是为什么我们的行业花了20年或更多的时间来思考这是我们所有问题的答案,但过去15年我们看到字节代码编译需要阶段,这是一个重要的优于我们之前的工作。回顾过去,我们意识到浪费了多少时间本地编译所有东西都是手工制作。

答案 5 :(得分:1)

作为另一个数据点,the D programming language是GC,OO,并且比C ++高很多,同时仍然被编译为本机代码。

答案 6 :(得分:0)

我同意Chubbard的回答,我想补充一点,在OO语言中,类型信息对于通过虚拟机或最后一级编译器实现优化非常重要

答案 7 :(得分:0)

开发解释器比编译器更容易。

努力发展......:

翻译<字节码解释器< bytecode-jit-compiler<编译器到平台无关的语言<编译器对多机依赖性汇编

由于平台独立性,停止jit编译器的开发是一种普遍趋势。理论计算机科学的性能和研究方面的首选语言将在所有可能的方向上开发,包括新的字节码解释器,即使有平台独立语言的优秀和高级编译器以及不同的机器相关汇编器。

OOP语言的研究非常简单......与函数式语言相比,让我们说乏味,因为真正的新语言和编译器技术更容易使用数学导管理论和数学描述的巡回完整类型表达 - 系统。换句话说:它本身几乎是功能性的,而命令式语言几乎只是具有一些语法糖的汇编语言。 OOP语言往往是命令式语言,因为函数式语言已经有闭包和lambda。还有其他方法可以在函数式语言中实现类似java的“接口”,而且不需要额外的面向对象的功能。

在Haskell中,添加类似OOP的编程功能可能不仅仅是技术上的几步 - 使用它没有意义。 (< - 那不仅仅是恕我直言......你听说过GADT或多参数类型?)可能有更好的方法来动态创建带接口的对象与OOP语言进行通信而不是改变那种语言本身。但是,还有其他功能语言明确地将功能和OOP方面结合起来。与非功能性OO语言相比,主要是函数式语言的科学更多。

OO语言不能轻易编译成其他OO语言,如果它们在某种程度上更“高级”。通常,它们具有堆栈保护器,高级调试功能,抽象和可检查的多线程,来自Internet的文件的动态对象加载等功能......使用C或C ++作为编译器,很多这些功能都不易实现或不易实现-backend。功能语言LISP(已有50年历史了!)是AFAIK第一个使用垃圾收集器的语言。由于编译器后端LISP使用了语言C的黑客版本,因为普通C不允许其中某些东西,汇编程序确实允许,即正确的尾调用或表旁边的代码。 C--允许这样做。

另一方面:命令式语言旨在在特定体系结构上运行,即C和C ++程序仅在那些体系结构上运行,它们是为其编程的。 Java更为极端:它只在一个体系结构上运行,一个虚拟体系结构本身在其他体系结构上运行。 功能语言通常在设计上非常独立于架构:LISP被开发为如此巨大的架构 - 非特定的,它可以在一些遥远的未来编译成遗传密码。是的,就像活在生物细胞中的程序一样。

使用LLVM的字节码,函数式语言最有可能在将来被编译为字节码。大多数命令式语言很可能仍然具有与现在非抽象相同的遗传问题。好吧,我对clang和D不太确定,但不管怎样这两个都不是“最”。