实现 ART 即时编译器

Android 运行时 (ART) 包含一个即时 (JIT) 编译器,它带有代码分析功能,可以持续改进 Android 应用程序在运行时的性能。JIT 编译器是 ART 当前的预先 (AOT) 编译器的补充,可以提高运行时性能、节省存储空间并加快应用程序和系统更新速度。它还改进了 AOT 编译器,避免了在自动应用程序更新或无线 (OTA) 更新期间重新编译应用程序时系统速度减慢的情况。

尽管 JIT 和 AOT 使用相同的编译器和类似的一组优化,但生成的代码可能不完全相同。JIT 利用运行时类型信息,可以更好地进行内联,并使堆栈上替换 (OSR) 编译成为可能,所有这些都会生成略有不同的代码。

JIT 架构

JIT architecture
图 1. JIT 架构。

JIT 编译

JIT 编译涉及以下活动

Profile-guided comp
图 2. 基于配置文件的编译。
  1. 用户运行应用,然后触发 ART 加载 .dex 文件。
    • 如果 .oat 文件(.dex 文件的 AOT 二进制文件)可用,ART 会直接使用它。尽管 .oat 文件会定期生成,但它们并不总是包含已编译的代码(AOT 二进制文件)。
    • 如果 .oat 文件不包含已编译的代码,ART 将通过 JIT 和解释器来执行 .dex 文件。
  2. 对于任何未根据 speed 编译过滤器(表示“尽可能多地编译应用程序”)编译的应用程序,JIT 都会启用。
  3. JIT 配置文件数据会转储到系统目录中的一个文件,该目录只有应用程序可以访问。
  4. AOT 编译 (dex2oat) 守护程序会解析该文件以驱动其编译。

    JIT daemon
    图 3. JIT 守护程序活动。

Google Play 服务是其他应用程序使用的示例,这些应用程序的行为类似于共享库。

JIT 工作流

JIT architecture
图 4. JIT 数据流。
  • 分析信息存储在代码缓存中,并在内存压力下进行垃圾回收。
    • 无法保证在应用程序在后台运行时拍摄的快照将包含完整的数据(即,所有已 JIT 编译的内容)。
    • 没有尝试确保记录所有内容(因为这会影响运行时性能)。
  • 方法可以处于三种不同的状态
    • 已解释(dex 代码)
    • JIT 编译
    • AOT 编译
    如果 JIT 和 AOT 代码都存在(例如,由于重复的反优化),则首选 JIT 编译的代码。
  • 在不影响前台应用性能的情况下运行 JIT 的内存要求取决于具体应用。大型应用比小型应用需要更多内存。一般来说,大型应用稳定在 4 MB 左右。

开启 JIT 日志记录

要开启 JIT 日志记录,请运行以下命令

adb root
adb shell stop
adb shell setprop dalvik.vm.extra-opts -verbose:jit
adb shell start

停用 JIT

要停用 JIT,请运行以下命令

adb root
adb shell stop
adb shell setprop dalvik.vm.usejit false
adb shell start

强制编译

要强制编译,请运行以下命令

adb shell cmd package compile

强制编译特定软件包的常见用例

  • 基于配置文件
    adb shell cmd package compile -m speed-profile -f my-package
    
  • 完全
    adb shell cmd package compile -m speed -f my-package
    

强制编译所有软件包的常见用例

  • 基于配置文件
    adb shell cmd package compile -m speed-profile -f -a
    
  • 完全
    adb shell cmd package compile -m speed -f -a
    

清除配置文件数据

在 Android 13 或更早版本上

要清除本地配置文件数据并移除已编译的代码,请运行以下命令

adb shell pm compile --reset 

在 Android 14 或更高版本上

仅清除本地配置文件数据

adb shell pm art clear-app-profiles 

注意:与 Android 13 或更早版本的命令不同,此命令不会清除随应用安装的外部配置文件数据 (.dm)。

要清除本地配置文件数据并移除从本地配置文件数据生成的已编译代码(即,重置为安装状态),请运行以下命令

adb shell pm compile --reset 

注意:此命令不会移除从随应用安装的外部配置文件数据 (.dm) 生成的已编译代码。

要清除所有已编译的代码,请运行此命令

adb shell cmd package compile -m verify -f 

注意:此命令会保留本地配置文件数据。