JIT 编译器如何优化代码
在选中某个方法进行编译时,JVM 会将其字节码提供给 Just-In-Time (JIT) 编译器。 JIT 必须了解字节码的语义和语法,才能正确编译方法。
为帮助 JIT 编译器分析方法,首先在名为树(与字节码相比,这更加类似于机器码)的内部表示中重新表示字节码。 然后,对方法树执行分析和优化。 最后,树将转换为本机代码。 本节中的其余部分提供了对 JIT 编译阶段的简要概述。 有关更多信息,请参阅 诊断 JIT 或 AOT 问题。
JIT 编译器可以使用多个编译线程来执行 JIT 编译任务。 使用多个线程可能会帮助 Java 应用程序更快地启动。 实际上,只有系统中存在未使用的处理核心时,使用多个 JIT 编译线程才能提高性能。
缺省编译线程数由 JVM 确定,并取决于系统配置。 如果生成的线程数不是最佳值,那么您可以使用 -XcompilationThreads 选项来覆盖 JVM 决策。 有关使用此选项的信息,请参阅 -X 选项。
注: 如果系统没有未使用的处理核心,那么增加编译线程数不太可能产生性能改进。
编译包括以下几个阶段。 除本机代码生成之外,所有阶段都是跨平台代码。
阶段 1 - 内联
内联是将较小方法树并入,或“内联”到其调用者树的过程。 这可加快对经常执行的方法的调用速度。 根据当前优化级别,使用具有不同活跃级别的两种内联算法。 此阶段执行的优化包括:
- 细琐内联
- 调用图内联
- 尾部递归消除
- 虚拟调用监管优化
阶段 2 - 局部优化
布局优化一次分析并改进一小部分代码。 多个局部优化实现尝试并测试了在一流的静态编译器中使用的技术。 优化包括:
- 局部数据流分析和优化
- 寄存器使用优化
- Java 成语的简单化
阶段 3 - 控制流优化
控制流优化分析方法内部的控制流(或者特定部分)并重新安排代码路径以提高效率。 优化包括:
- 代码重新排序、分割和移除
- 循环缩减和循环反演
- 循环步幅和循环不变量代码移动
- 循环展开和剥离
- 循环版本控制和专用化
- 异常定向优化
- 转换分析
阶段 4 - 全局优化
全局优化立即对整个方法生效。 它们的“成本更高”,需要更多的编译时间,但可显著提高性能。 优化包括:
- 全局数据流分析和优化
- 部分冗余消除
- 逃逸分析
- GC 和内存分配优化
- 同步优化
阶段 5 - 本机代码生成
本机代码生成过程根据平台体系结构的不同而有所不同。 通常,在此编译阶段中,方法树将转换为机器码指令;并根据体系结构特征执行一些较小的优化。 已编译的代码将放入名为代码高速缓存的 JVM 过程空间的某一部分中;将记录代码高速缓存中方法的位置,以供今后针对调用已编译的代码而调用。 在任何指定的时间,JVM 过程由 JVM 可执行文件和一组 JIT 编译的代码组成,这些代码动态连接到 JVM 中的字节码解释器。