参考文档:【Java核心技术卷】Java的解释执行与编译执行
1. JVM 简介
一种能够运行 Java 字节码 (class) 的虚拟机。
JVM 有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有一套字节码指令集。JVM 屏蔽了与具体操作系统平台相关的信息,使得Java 程序只需要生成在 Java 虚拟机上运行的目标代码(class 字节码),就可以在多种平台上不加修改地运行。JVM 是 Java 平台无关的基础。
JVM 负责运行字节码:JVM 把每一条要执行的字节码交给解释器,翻译成对应的机器码,然后由解释器执行。JVM 解释执行字节码文件就是 JVM 操作 Java 解释器进行解释执行字节码文件的过程。
为什么需要解释执行?
class 是编译完的字节码,它是不能直接运行的,jvm 需要将 class 字节码解释为 当前操作系统认识的机器码,才能被执行。JVM 也就是利用这种方式,来实现跨平台的特性。
HotSpot
HotSpot VM 是 Oracle JDK 和 OpenJDK 中所带的虚拟机,也是目前使用范围最广的 Java 虚拟机。
HotSpot 采用解释执行(java 解释器) 和编译执行(JIT 编译器) 两种方式,来将字节码解释为机器码,下文详解。
2. java 代码的执行
java 从源码到执行分为三个阶段
- javac 将源码编译为 class
- ClassLoader 装在 class,将 class 装载进方法区
- 执行 class(解释执行和编译执行)
本文我们主要对执行过程进行讲解。
2.1 基本概念
字节码:字节码是已经经过编译(.class文件),但与特定机器码无关,需要解释器转译后才能成为机器码的中间代码。
Java 字节码:是 Java 虚拟机执行的一种指令格式。
Java 编译器:将 Java 源文件(.java文件)编译成字节码文件(.class文件,是特殊的二进制文件,二进制字节码文件),这种字节码就是 JVM 的“机器语言”。javac.exe 可以简单看成是 Java 编译器。
Java 解释器:是一种电脑程序,能够把高级编程语言一行一行直接翻译运行。解释器不会一次把整个程序翻译出来,只像一位“中间人”,每次运行程序时都要先转成另一种语言再作运行,因此解释器的程序运行速度比较缓慢。它每翻译一行程序叙述就立刻运行,然后再翻译下一行,再运行,如此不停地进行下去。
JIT 编译器(Just-in-time compilation):注意与解释器区分。JIT 编译器是 JRE 的一部分。原本的 Java 程序都是要经过解释执行的,其执行速度肯定比可执行的二进制字节码程序慢。为了提高执行速度,引入了 JIT。在运行时,JIT 会把翻译过来的机器码保存起来,以备下次使用。而如果 JIT 对每条字节码都进行编译,则会负担过重,所以,JIT 只会对经常执行的字节码进行编译,如循环,高频度使用的方法等。它会以整个方法为单位,一次性将整个方法的字节码编译为本地机器码,然后直接运行编译后的机器码,程序运行较快。
2.2 编译执行和解释执行
如今的 HotSpot VM 中不仅内置有解释器,还内置有先进的JIT(Just In Time Compiler)编译器,在 Java 虚拟机运行时,解释器和即时编译器能够相互协作,各自取长补短。有一点需要注意,无论是采用解释器进行解释执行,还是采用即时编译器进行编译执行,最终字节码都需要被转换为对应平台的本地机器指令。
既然 HotSpot VM 中已经内置 JIT 编译器了,那么为什么还需要再使用解释器来“拖累”程序的执行性能呢?
对于服务端应用来说,启动时间并非是关注重点,但对于那些看中启动时间的应用场景而言,或许就需要采用解释器与即时编译器并存的架构来换取一个平衡点。
由于即时编译器将本地机器指令的编译推迟到了运行时,自此 Java 程序的运行性能已经达到了可以和 C/C++ 程序一较高下的地步。这主要是因为 JIT 编译器可以针对那些频繁被调用的“热点代码”做出深度优化,而静态编译器的代码优化则无法完全推断出运行时热点,因此通过 JIT 编译器编译的本地机器指令比直接生成的本地机器指令拥有更高的执行效率也就理所当然了。