概述
什么是执行引擎?
定义:[采用 软件模拟 的物理机处理器就是执行引擎]{.red}
- 物理机中的计算核心是处理器(CPU)
- 虚拟机作为对物理机的模拟,执行引擎显然就是对计算核心的模拟,也就是处理器
作用:[将(前端)编译器生成的字节码 翻译/编译 成本地机器指令]{.red}
- 执行引擎每次都取出一条字节码指令并执行
- 但是无论执行任何指令,最终都要依靠物理机,所以需要向物理机解释
- 执行引擎需要将字节码指令翻译/编译成处理器能够识别的机器指令
:::info
① 前端编译器是什么之后将会提到,可以暂时先认为前端编译器就是 Javac
② 前端编译器并不是执行引擎的其中一部分,特别注意
:::
特点:
[执行引擎作为对于处理的模拟:具有相应的指令集和缓存结构]{.red}
- 执行引擎的指令集:[字节码指令]{.blue}
- 执行引擎的缓存结构:[即时编译器产生的本地代码就缓存再方法区中]{.blue}
执行引擎处理指令集的方式:
- 解释执行:初代 Java 虚拟机——Classic 虚拟机就只拥有解释器,仅能够解释执行
- 即时编译:BEA 开发的 JRockit 虚拟机就仅具有即时编译器,只能够编译执行
- [解释执行 + 即时编译:HotSpot 虚拟机既具有即时编译器和解释器,两者可以协调合作]{.red}
:::info
注:什么是解释执行,什么是即时编译之后会详细提到
:::
[Java 虚拟机执行引擎是基于栈执行的,现代计算机基本都是基于寄存器执行的]{.red}
:::primary
想要了解什么是基于栈执行的执行引擎,参考 运行时数据区-栈空间-操作数栈 部分
:::
执行引擎是由哪些部分组成的?
:::warning
① 仅针对 HotSpot 虚拟机的执行引擎,而不针对其他虚拟机的执行引擎
② 下面的图示是将执行引擎的大多数细节都填充进去了,目前你只需要知道个大致组成就行,之后会详细介绍
:::
图示
组成:[解释器 + 即时编译器 + 垃圾回收器]{.red}
解释器(解释执行)与编译器(编译执行)到底是什么?
:::warning
① 如果想要详细了解编译器,建议阅读《编译原理》(俗称龙书)
② 如果想要了解机器指令和汇编语言,建议阅读《深入理解计算机系统》和《数字设计和计算机体系结构》
:::
编译器(编译执行):
定义:[编译器将高级语言编译成处理器能够识别的机器指令]{.red}
主要语言:C、C++ 都是编译执行的语言
流程(任何类型的编译器基本都遵照以下流程):
分类(仅限于 Java):
前端编译器:
- 定义:[负责将源代码编译成 字节码指令]{.red}
- 常见编译器:JDK 自带的 Javac 编译器,Eclipse JDT 提供的 ECJ(增量式编译器)
- [Javac 编译器完全采用 Java 实现]{.blue}
- Eclipse 编辑器中使用的就是 ECJ 编译器
即时编译器(后端编译器)
定义:[负责在进程运行时将经常调用的方法编译成 机器指令]{.red}
- [编译生成的本地代码将会被缓存再方法区中(JIT 代码缓存)]{.red}
常见编译器:HotSpot 提供的C1(客户端编译器)、C2(服务器端编译器),Graal 虚拟机提供的 Graal 编译器
Graal 目前还只是实验性质的虚拟机,并没有发布正式的版本
提前编译器(后端编译器)
- 定义:[直接 将源代码编译成机器指令]{.red}
- 类似于 C/C++ 使用的 GCC 编译器
- 常见编译器:JDK 自带的 Jaotc 编译器,GNU 计划中的 GCJ 编译器,Excelsior JET 编译器
- 定义:[直接 将源代码编译成机器指令]{.red}
解释器(解释执行):
定义:[解释器每次都会向处理器解释源代码的含义]{.red}
主要语言:Python、JavaScript、PHP 都是解释执行的语言
流程
分类(仅限于 Java):
- [字节码解释器:采用纯软件模拟的方式执行字节码 -> 效率非常低下]{.red}
- [模板解释器:每条字节码都和一个模板函数关联,模板函数能够直接生成字节码对应的本地代码 -> 效率相对较高]{.red}
区别:
- [编译器对源代码进行编译之后会生成可执行文件,解释器不会生成任何可执行文件]{.red}
- [编译器在编译过程中会对源代码采用各种优化技术进行优化(诸如逃逸分析),解释器不会对源代码进行任何优化]{.red}
- 源代码再次执行时:
- 编译器生成的可执行文件就可以直接使用不需要再编译,[所以编译执行效率非常高]{.blue}
- 解释器则需要再次向处理器解释源代码的含义,[所以解释执行的效率就低]{.blue}
- [编译器需要对源代码进行编译:程序的启动速度慢;解释器不需要任何编译过程直接向处理器解释:启动速度快]{.red}
+++danger Java 到底是解释执行还是编译执行的语言呢?
① 远古时代的 Java 确实是纯解释执行的语言,就和 Python 一样
② 如今的 Java 既不是纯解释执行也不是纯编译执行的语言,而是半解释半编译的语言
③ 如今的 Java 程序在运行过程会交替使用解释器和编译器,使得运行效率大幅提升(至于如何交替使用,会在后端编译器部分中详细介绍)
+++
执行引擎如何使用解释器和即时编译器呢?
[前端编译器将源代码编译成字节码]{.red}
- 提前编译器通常会给即时编译器作为缓存加速,改善程序的启动时间
执行引擎获取程序计数器提供的指令地址
执行引擎从方法区中获取相应的字节码指令
[执行引擎开始协调解释器和即时编译器执行:]{.red}
[使用 热点探测技术 判断字节码指令是否要编译成本地代码]{.blue}
如果执行引擎决定将其编译为本地代码,那么即时编译器将开始执行编译过程
编译期间,[解释器会和即时编译器 异步执行]{.blue},避免浪费用户线程的执行时间
如果执行引擎不认为要将其编译为本地代码,那么就采用解释执行
查看调用的方法是否存在编译好的本地代码
- 如果被调用的方法没有被编译成本地代码,那么将会解释执行
- 如果被调用的方法已经有相应的本地代码,那么将会直接执行本地代码

+++danger 最后抛出一个问题,为什么要使用字节码文件呢?明明虚拟机已经实现跨平台了啊?
+++