(接上篇)
四、执行引擎子系统
(一)机器码 -> 指令集 -> 汇编语言 -> 高级语言
1、机器码
二进制编码方式表示的指令;
2、指令集
(1)指将机器码中特定的0和1组成的序列,简化为对应的指令,如INC、DEC、MOV等;
(2)每个平台所支持的指令被称为对应平台的指令集(X86、ARM等);
3、汇编语言
(1)用助记符(Mnemonics)代替机器指令的操作码,用地址符号(Symbol)以及标号(Label)代替指令或操作数的地址;
(2)在不同的平台,汇编代码对应不同的指令集;
(3)需要经过汇编阶段,变为计算机可识别的机器指令;
4、高级语言
(1)可读性更高,编写难度更低(Java、Python、Go等);
(2)需要先经过解释或编译过程,翻译成汇编指令,然后再经过汇编过程,转换为计算机可识别的机器指令码才能执行;
(二)执行引擎概述
1、主要任务
将字节码指令解释/编译成对应平台上的本地机器指令;
2、执行技术
(1)解释执行:第一代JVM;
(2)即时编译JIT:第二代JVM;
(3)自适应优化:吸取第一代JVM和第二代JVM的经验,采用两者结合的方式;
(三)执行引擎工作过程
1、源码编译阶段(前端编译)
(1)由前端编译器javac完成;
(2)过程:Java源代码 -> (词法分析器) -> Token流 -> (语法分析器) -> 抽象语法树 -> (语义分析器) -> 语法树 -> (字节码生成器) -> JVM字节码;
2、类加载阶段(上篇笔记记录)
类加载器将JVM字节码加载进内存;
3、执行引擎执行阶段
(1)后端编译:将字节码指令翻译成本地机械指令交由物理机的执行引擎来真正的执行;
(2)过程:JVM字节码 -> (编译器代码优化) -> 中间代码 -> (处理器指令优化) -> 中间代码 -> (寄存器分配优化) -> 中间代码 -> (目标代码生成器) -> 本地机器指令;
(四)执行引擎构成原理
1、解释器(Interpreter)
在执行一个方法或某处代码时:
(1)找到.class文件中对应的字节码;
(2)对每条需执行的字节码指令逐行解释,将其翻译成平台对应对应的本地机械码执行;
(3)当一条字节码指令被解释执行完成后,紧接着会根据PC寄存器(程序计数器)中记录的下一条需被执行指令,读取并再次进行解释执行操作;
2、JIT即时编译器(Just In Time Compiler)
(1)热点代码探测技术:栈上替换(On Stack Replacement)、方法调用计数器(Invocation Counter)、回边计数器(BackEdge Counter)、热度衰减(Counter Decay);
(2)对热点代码进行深度优化,并将该方法直接编译成当前平台对应的机器码;
(3)冷热机流量迁移:
a、采用更多的机器承载热机状态过来的流量,等后续这些刚启动的冷机变成热机状态了,可以再把多余的机器停掉;
b、使用网关控制流量,先将一部分流量转发给刚启动的冷机,让冷机先做预热,然后将原本计划的所有流量迁移到这些机器;
c、在当日流量最小的时间段进行切换;
3、JIT实例(64位系统默认为C2,JDK1.6之后开启分层编译策略)
(1)C1(Client Compiler):启动参数-client,对字节码进行简单和可靠的优化,耗时比较短,追求编译速度;
(2)C2(Server Compiler):启动参数-server,对字节码进行激进优化,耗时比较长,追求编译后的执行性能;
(3)新一代编译器:Graal;
(五)分派调用(Dispatch)
1、方法分派调用(方法绑定)
(1)目的:确定被调用方法的版本,要具体调用重载、重写情况下的哪一个方法;
(2)不会涉及到方法体中逻辑的执行;
(3)过程:方法调用存储在.class文件都是符号引用,不是直接引用,经过类加载的解析阶段转换为直接引用;
(4)指令:invokestatic、invokespecial、invokevirtual、invokeinterface、invokedynamic;
2、静态分派
(1)发生在编译期,由编译器执行分派动作;
(2)典型体现:方法重载(Overload);
3、动态分派
(1)在编译期无法通过静态类型判定出方法版本,需要在运行期间由虚拟机来判定方法调用的具体版本;
(2)典型体现:方法重写(Override);