Files
Hui-s-notebook/即时编译器JIT.md
2023-09-10 10:50:53 +08:00

3.4 KiB
Raw Permalink Blame History

解释器与编译器

HotSpot 中内置了两个 (或三个)即时编译器, "客户端编译器/C 1 编译器" "服务端编译器/C 2 编译器" 代替 C 2 的 Graal 编译器

分层编译之前, 采用解释器和其中一个编译器直接搭配的方式, 使用哪个编译器,取决于虚拟机运行的模式, HotSpot 会根据自身版本与宿主机的硬件性能自动选择运行模式, 也可使用指定参数"-client""-server"强制指定虚拟机运行模式

编译器与解释器搭配的方式在虚拟机中称为"混合模式" 使用"-Xint"强制"解释模式" 使用"-Xcomp"强制"编译模式" 但是解释器仍然要在编译无法进行的情况下介入执行过程, 使用虚拟机"-version"指令可以输出该结果

由于即时编译器编译本地代码需要占用程序运行时间, 要编译出优化程序越高的代码,时间越长, 编译出优化程度高的代码,解释器可能还要替编译器收集性能监控信息, 因此在编译子系统中加入了分层编译,包括:

- 0 程序纯解释执行,解释器不开启性能监控功能
- 1 使用客户端编译器将字节码编译为本地代码运行,进行简单可靠稳定优化,不开启监控
- 2 使用客户端编译器执行,开启方法及回边(循环边界往回跳)次数统计等有限监控
- 3 使用客户端编译器,开启全部性能监控
- 4 使用服务器编译器将字节码编译为本地代码,启用更多编译耗时长的优化,根据监控信息进行一些不可靠的激进优化

编译对象与触发条件

热点代码有两类:

- 被多次调用的方法
- 被多次调用的循环体

对于这两种情况, 编译的目标都是整个方法体,而不是单独循环体, 编译器以整个方法作为编译对象, 只是执行入口稍有不同, 编译时传入制定入口点字节码序号, 这种编译方式称为"栈上替换" 方法的栈帧还在栈上,方法被替换了

判断某段是否为热点代码,是否触发即时编译,称为"热点预测" 主流探测判定方式有两种:

- 基于采样的热点探测
	周期性检查线程调用栈顶,
	某个方法经常出现在栈顶,就是热点方法
- 基于计数器的热点探测
	为每个方法和代码块建立计数器,
	HotSpot准备了方法调用计数器和回变计数器
	执行次数超过一定阈值即为热点方法

编译过程

虚拟机在编译器未完成编译前,都按解释方式执行, 编译动作在后台的编译线程中进行

后台执行时,客户端编译器是一个简单快速的三段式编译器

第一阶段, 平台独立的前端将字节码构造成一种高级中间代码表示 (HIR) 第二阶段, 一个平台相关的后端从 HIR 产生低级中间代码表示 (LIR), 第三阶段, 平台相关的后端使用线性扫描算法在 LIR 上分配寄存器, 在 LIR 上做窥孔优化, 然后产生机器代码

服务器编译器是专门面向服务端的典型应用场景, 并为服务端的性能配置针对性调整过的编译器, 也是一个能容忍很高优化复杂度的高级编译器, 会执行大部分经典优化动作: 无用代码消除、 循环展开、 循环表达式外提、 消除公共子表达式、 常量传播、 基本块重排序、 范围检查消除、 空值检查消除等