1.6 Java执行机制

用Java语言编写的程序是怎样被运行的呢?本节将为大家讲解Java的运行机制,在讲解之前我们需要先了解一下编译型语言和解释型语言。我们都知道计算机是一个0和1的世界,计算机运行时根据一串0和1组成的指令来执行操作。我们称这些二进制指令为机器语言,它们对人类是十分不友好的,因为我们根本无法直接使用0和1进行编程。那么该怎么办呢?答案就是制定人类能看懂的编程语言(我们称之为高级语言),然后引入一种翻译机制将高级语言翻译成计算机语言,最后由计算机执行,如图1.10所示。

图1.10 程序运行机制

根据翻译的方式可以分为编译和解释,这两种方式的差异就在于翻译的时间不同。

对于编译型语言,它需要一个专门的编译过程,将高级语言编译成机器语言二进制文件。比如将C++代码编译成.exe二进制文件,这个文件就是由机器语言构成的。程序编译后每次运行时都可以避免再次编译,直接就可以运行,执行效率高。

从高级语言到机器语言的实际编译过程如图1.11所示,高级语言先编译成汇编语言,汇编语言再进一步编译成机器语言,最后是计算机执行这些机器语言。举个例子,高级语言中“a = b”(即将变量b赋值给变量a),经过编译后变成汇编语言“mov a,b”,然后再一次编译成机器指令“1000100111011000”。

图1.11 编译型语言运行

而对于解释型语言来说,它不需要事先执行编译工作,它把翻译的工作推后了,每次运行程序时都需要翻译一遍。比如shell脚本语言就是解释型语言,当我们编写完代码后不需要编译就能直接在计算机上解释执行,实际上就是在运行时由一个解释器将其翻译成机器语言并执行,如图1.12所示。由于每次执行都要使用解释器将高级语言翻译成机器语言,因此解释性语言的执行效率会比较低。

图1.12 解释型语言运行

上述两种类型语言的优缺点可以从以下两个方面来讨论。

执行效率。编译型语言明显更胜一筹,它在执行前一次性将所有高级语言都翻译成机器语言并保存起来,而解释型语言由于每次执行都需要翻译,因此效率低。

跨平台性。解释型语言具有更好的跨平台性,只需要支持不同硬件和操作系统的解释器就能运行同一套高级语言代码。而编译型语言在运行前会先将高级语言编译成指定硬件和操作系统的机器指令并保存成二进制文件,如果想在其他操作系统中运行,则需要先重新编译成其他操作系统的机器指令并保存成二进制文件。

Java 语言属于解释型还是编译型呢?实际上它将两者进行了结合,属于既包含解释又包含编译的混合型语言。Java在编译时并非直接编译成机器语言(机器指令),而是编译为中间语言(Java字节码指令),然后由解释器(JVM)解析并执行。由于中间语言是相同的,而不同操作系统和硬件的机器语言却是不同的,因此必须根据操作系统和硬件来实现不同的解释器。通过图1.13可以很清晰看出Java语言的编译与解释过程。

图1.13 Java语言运行过程

假如我们编写了一个hello.java文件,经过编译后成为hello.class文件,该文件保存的就是Java字节码,然后由不同操作系统的JVM解释执行,如图1.14所示。

图1.14 Java语言运行示例

如果Java语言仅通过解释器解析执行则执行效率较低,为了能提高执行效率,Java引入了即时(Just-In-Time,JIT)编译技术。之所以叫“即时编译”,是因为是在执行的时候进行编译的,把原来的Java字节码编译成机器指令,而且将这些机器指令缓存起来下次直接由计算机执行,从而有效避免解释工作,如图1.15所示。需要注意的是,并非每条字节码都会被即时编译器编译缓存起来,毕竟缓存也是需要成本的,只有那些高频的热点代码才会被即时编译器编译并缓存。

图1.15 Java编译机制

考考你

高级语言需要翻译成什么语言,计算机才能执行?大概过程是怎样的?

根据编译方式,编程语言可以分成哪两种?

编译型语言和解释型语言的各自优缺点是什么?

Java是什么类型的语言?说说它的编译和解释的过程。

“Java编译器会根据不同的操作系统编译成不同的中间码”。这种说法正确吗?

为了提高执行效率,Java使用了什么技术进行优化?

Java即时编译器会将所有字节码编译成机器码并缓存起来吗?