JAVA到底是解释型语言还是编译型语言

转载自:http://rednaxelafx.iteye.com/blog/492667

以及知乎上觉浅的回答:http://www.zhihu.com/question/19608553

这个博主也是大牛啊,我这里先截取这个主题的主要结论。

(1)解析器(parser):前者是编译器/解释器的重要组成部分,也可以用在IDE之类的地方;其主要作用是进行语法分析,提取出句子的结构。广义来说输入一般是程序的源码,输出一般是语法树(syntax tree,也叫parse tree等)抽象语法树(abstract syntax tree,AST)。进一步剥开来,广义的解析器里一般会有扫描器(scanner,也叫tokenizer或者lexical analyzer,词法分析器),以及狭义的解析器(parser,也叫syntax analyzer,语法分析器)。扫描器的输入一般是文本,经过词法分析,输出是将文本切割为单词的流。狭义的解析器输入是单词的流,经过语法分析,输出是语法树或者精简过的AST。


(2)解释器(intepretor):后者则是实现程序执行的一种实现方式,与编译器相对。它直接实现程序源码的语义,输入是程序源码,输出则是执行源码得到的计算结果;编译器的输入与解释器相同,而输出是用别的语言实现了输入源码的语义的程序。通常编译器的输入语言比输出语言高级,但不一定;也有输入输出是同种语言的情况,此时编译器很可能主要用于优化代码。


举例:把同样的源码分别输入到编译器与解释器中,得到的输出不同:



(3)虚拟机的编译与解释:VM并不是神奇的就能执行代码了,它也得采用某种方式去实现输入程序的语义,并且同样有几种选择:“编译”,例如微软的.NET中的CLR;“解释”,例如CPython、CRuby 1.9,许多老的JavaScript引擎等;也有介于两者之间的混合式,例如Sun的JVM,HotSpot。如果采用编译方式,VM会把输入的指令先转换为某种能被底下的系统直接执行的形式(一般就是native code),然后再执行之;如果采用解释方式,则VM会把输入的指令逐条直接执行。
换个角度说,我觉得采用编译和解释方式实现虚拟机最大的区别就在于是否存下目标代码:编译的话会把输入的源程序以某种单位(例如基本块/函数/方法/trace等)翻译生成为目标代码,并存下来(无论是存在内存中还是磁盘上,无所谓),后续执行可以复用之;解释的话则是把源程序中的指令逐条解释,不生成也不存下目标代码,后续执行没有多少可复用的信息。

如果一种语言的主流实现是解释器,其内部是编译器+虚拟机,而虚拟机又是采用解释方式实现的,或者内部实现是编译器+树遍历解释器,那它就是名副其实的“解释型语言”。


(4)作者的最终结论:Well……其实写Java程序不也是这样么?现在也确实还有很多人把Java称为“解释型语言”,完全无视Java代码通常是经过显式编译步骤才得到.class文件,而有些JVM是采用纯JIT编译方式实现的,内部没解释器,例如JRockitMaxine VMJikes RVM我愈发感到“解释型语言”是个应该避开的用语 =_=(3)


再加上知乎上的一个回答,结论就比较明显了:

JAVA的第一道工序是javac编译,当然目标文件是BYTECODE。后续可能有三种处理方式:
1. 运行时,BYTECODE由JVM逐条解释执行,
2. 运行时,部分代码可能由JIT翻译为目标机器指令(以method为翻译单位,还会保存起来,第二次执行就不用翻译了)直接执行;

3. RTSJ。继JAVAC之后执行AOT二次编译,生成静态的目标平台代码(典型的就是IBM WEBSHPERE REAL TIME)。



原文内容:

(Disclaimer:如果需要转载请先与我联系;文中图片请不要直接链接
作者:@RednaxelaFX ->http://rednaxelafx.iteye.com

大前天收到一条PM:

引用
你好,很冒昧的向你发短消息,我现在在看JS引擎,能过看博客发现你对js engine很了解,我想请教一下你 基于栈的解析器与基于寄存器的解析器有什么同,javascriptcore是基于寄存器的,V8是基于栈的,能不能说一下这两者有什么一样吗?能推荐一点资料吗?谢谢。

我刚收到的时候很兴奋,就开始写回复。写啊写发觉已经比我平时发的帖还要长了,想着干脆把回复直接发出来好了。于是下面就是回复:

你好 ^ ^ 很抱歉拖了这么久才回复。码字和画图太耗时间了。
别说冒昧了,我只是个普通的刚毕业的学生而已,担当不起啊 =_=||||
而且我也不敢说“很”了解,只是有所接触而已。很高兴有人来一起讨论JavaScript引擎的设计与实现,总觉得身边对这个有兴趣的人不多,或者是很少冒出来讨论。如果你发个帖或者blog来讨论这方面的内容我也会很感兴趣的~

想拿出几点来讨论一下。上面提出的问题我希望能够一一给予回答,不过首先得做些铺垫。
另外先提一点:JavaScriptCore从SquirrelFish版开始是“基于寄存器”的,V8则不适合用“基于栈”或者“基于寄存器”的说法来描述。

1、解析器与解释器

解析器是 parser,而解释器是 interpreter。两者不是同一样东西,不应该混用。

前者是编译器/解释器的重要组成部分,也可以用在IDE之类的地方;其主要作用是进行语法分析,提取出句子的结构。广义来说输入一般是程序的源码,输出一般是 语法树(syntax tree,也叫parse tree等)抽象语法树(abstract syntax tree,AST)。进一步剥开来,广义的解析器里一般会有扫描器(scanner,也叫tokenizer或者lexical analyzer,词法分析器),以及狭义的解析器(parser,也叫syntax analyzer,语法分析器)。扫描器的输入一般是文本,经过词法分析,输出是将文本切割为单词的流。狭义的解析器输入是单词的流,经过语法分析,输出是语法树或者精简过的AST。
(在一些编译器/解释器中,解析也可能与后续的语义分析、代码生成或解释执行等步骤融合在一起,不一定真的会构造出完整的语法树。但概念上说解析器就是用来抽取句子结构用的,而语法树就是表示句子结构的方式。关于边解析边解释执行的例子,可以看看 这帖的计算器。)
举例:将i = a + b * c作为源代码输入到解析器里,则广义上的解析器的工作流程如下图:

其中词法分析由扫描器完成,语法分析由狭义的解析器完成。
(嗯,说来其实“解析器”这词还是按狭义用法比较准确。把扫描器和解析器合起来叫解析器总觉得怪怪的,但不少人这么用,这里就将就下吧 =_=
不过近来“ scannerless parsing”也挺流行的:不区分词法分析与语法分析,没有单独的扫描器,直接用解析器从源码生成语法树。这倒整个就是解析器了,没狭不狭义的问题)

后者则是实现程序执行的一种实现方式,与编译器相对。它直接实现程序源码的语义,输入是程序源码,输出则是执行源码得到的计算结果;编译器的输入与解释器相同,而输出是用别的语言实现了输入源码的语义的程序。通常编译器的输入语言比输出语言高级,但不一定;也有输入输出是同种语言的情况,此时编译器很可能主要用于优化代码。
举例:把同样的源码分别输入到编译器与解释器中,得到的输出不同:

值得留意的是,编译器生成出来的代码执行后的结果应该跟解释器输出的结果一样——它们都应该实现源码所指定的语义。

在很多地方都看到解析器与解释器两个不同的东西被混为一谈,感到十分无奈。
最近某本引起很多关注的书便在开篇给读者们当头一棒,介绍了“ JavaScript解析机制”。“编译”和“预处理”也顺带混为一谈了,还有“预编译” 0_0
我一直以为“预编译”应该是 ahead-of-time compilation的翻译,是与“即时编译”(just-in-time compilation,JIT)相对的概念。另外就是PCH(precompile header)这种用法,把以前的编译结果缓存下来称为“预编译”。把AOT、PCH跟“预处理”( preprocess)混为一谈真是诡异。算了,我还是不要淌这浑水的好……打住。


2、“解释器”到底是什么?“解释型语言”呢?

很多资料会说,Python、Ruby、JavaScript都是“解释型语言”,是通过解释器来实现的。这么说其实很容易引起误解:语言一般只会定义其抽象语义,而不会强制性要求采用某种实现方式。
例如说C一般被认为是“编译型语言”,但C的解释器也是存在的,例如 Ch。同样,C++也有解释器版本的实现,例如 Cint
一般被称为“解释型语言”的是主流实现为解释器的语言,但并不是说它就无法编译。例如说经常被认为是“解释型语言”的 Scheme就有好几种编译器实现,其中率先支持 R6RS规范的大部分内容的是 Ikarus,支持在x86上编译Scheme;它最终不是生成某种虚拟机的字节码,而是直接生成x86机器码。

解释器就是个黑箱,输入是源码,输出就是输入程序的执行结果,对用户来说中间没有独立的“编译”步骤。这非常抽象,内部是怎么实现的都没关系,只要能实现语义就行。你可以写一个C语言的解释器,里面只是先用普通的C编译器把源码编译为in-memory image,然后直接调用那个image去得到运行结果;用户拿过去,发现直接输入源码可以得到源程序对应的运行结果就满足需求了,无需在意解释器这个“黑箱子”里到底是什么。
实际上很多解释器内部是以“编译器+虚拟机”的方式来实现的,先通过编译器将源码转换为AST或者字节码,然后由虚拟机去完成实际的执行。所谓“解释型语言”并不是不用编译,而只是不需要用户显式去使用编译器得到可执行代码而已。

那么虚拟机( virtual machine,VM)又是什么?在许多不同的场合,VM有着不同的意义。如果上下文是Java、Python这类语言,那么一般指的是高级语言虚拟机(high-level language virtual machine,HLL VM),其意义是实现高级语言的语义。VM既然被称为“机器”,一般认为输入是满足某种指令集架构( instruction set architecture,ISA)的指令序列,中间转换为目标ISA的指令序列并加以执行,输出为程序的执行结果的,就是VM。源与目标ISA可以是同一种,这是所谓same-ISA VM。
前面提到解释器中的编译器的输出可能是AST,也可能是字节码之类的指令序列;一般会把执行后者的程序称为VM,而执行前者的还是笼统称为解释器或者树遍历式解释器(tree-walking interpreter)。这只是种习惯而已,并没有多少确凿的依据。只不过线性(相对于树形)的指令序列看起来更像一般真正机器会执行的指令序列而已。
其实我觉得把执行AST的也叫VM也没啥大问题。如果认同这个观点,那么把 DLR看作一种VM也就可以接受了——它的“指令集”就是树形的Expression Tree。

VM并不是神奇的就能执行代码了,它也得采用某种方式去实现输入程序的语义,并且同样有几种选择:“编译”,例如微软的.NET中的CLR;“解释”,例如CPython、CRuby 1.9,许多老的JavaScript引擎等;也有介于两者之间的混合式,例如Sun的JVM, HotSpot。如果采用编译方式,VM会把输入的指令先转换为某种能被底下的系统直接执行的形式(一般就是native code),然后再执行之;如果采用解释方式,则VM会把输入的指令逐条直接执行。
换个角度说,我觉得采用编译和解释方式实现虚拟机最大的区别就在于是否存下目标代码:编译的话会把输入的源程序以某种单位(例如 基本块/函数/方法/trace等)翻译生成为目标代码,并存下来(无论是存在内存中还是磁盘上,无所谓),后续执行可以复用之;解释的话则是把源程序中的指令逐条解释,不生成也不存下目标代码,后续执行没有多少可复用的信息。有些稍微先进一点的解释器可能会优化输入的源程序,把满足某些模式的指令序列合并为“超级指令”;这么做就是朝着编译的方向推进。后面讲到解释器的演化时再讨论超级指令吧。

如果一种语言的主流实现是解释器,其内部是编译器+虚拟机,而虚拟机又是采用解释方式实现的,或者内部实现是编译器+树遍历解释器,那它就是名副其实的“解释型语言”。如果内部用的虚拟机是用编译方式实现的,其实跟普遍印象中的“解释器”还是挺不同的……

可以举这样一个例子:ActionScript 3,一般都被认为是“解释型语言”对吧?但这种观点到底是把FlashPlayer整体看成一个解释器,因而AS3是“解释型语言”呢?还是认为FlashPlayer中的虚拟机采用解释执行方案,因而AS3是“解释型语言”呢?
其实Flash或Flex等从AS3生成出来的SWF文件里就包含有AS字节码(ActionScript Byte Code,ABC)。等到FlashPlayer去执行SWF文件,或者说等到AVM2(ActionScript Virtual Machine 2)去执行ABC时,又有解释器和JIT编译器两种实现。这种需要让用户显式进行编译步骤的语言,到底是不是“解释型语言”呢?呵呵。所以我一直觉得“编译型语言”跟“解释型语言”的说法太模糊,不太好。
有兴趣想体验一下从命令行编译“裸”的AS3文件得到ABC文件,再从命令行调用AVM2去执行ABC文件的同学,可以从 这帖下载我之前从源码编译出来的AVM2,自己玩玩看。例如说要编译一个名为test.as的文件,用下列命令:
Command prompt代码   收藏代码
  1. java -jar asc.jar -import builtin.abc -import toplevel.abc test.as  

就是用ASC将test.as编译,得到test.abc。接着用:
Command prompt代码   收藏代码
  1. avmplus test.abc  

就是用AVM2去执行程序了。很生动的体现出“编译器+虚拟机”的实现方式。
这个“裸”的AVM2没有带Flash或Flex的类库,能用的函数和类都有限。不过AS3语言实现是完整的。可以用print()函数来向标准输出流写东西。
Well……其实写Java程序不也是这样么?现在也确实还有很多人把Java称为“解释型语言”,完全无视Java代码通常是经过显式编译步骤才得到.class文件,而有些JVM是采用纯JIT编译方式实现的,内部没解释器,例如 JRockitMaxine VMJikes RVM。我愈发感到“解释型语言”是个应该避开的用语 =_=

关于虚拟机,有本很好的书绝对值得一读, 《虚拟机——系统与进程的通用平台》(Virtual Machines: Versatile Platforms for Systems and Processes)。国内有影印版也有 中文版,我是读了影印版,不太清楚中文版的翻译质量如何。据说翻译得还行,我无法印证。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值