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 中的字节码解释器。