ART ジャストインタイム コンパイラを実装する

Android ランタイム(ART)には、実行中の Android アプリのパフォーマンスを継続的に改善するコード プロファイリングを備えた、ジャストインタイム(JIT)コンパイラが含まれています。JIT コンパイラは、ART の現行の事前(AOT)コンパイラを補完し、ランタイムのパフォーマンスの改善、ストレージ容量の節約、アプリとシステムのアップデートの高速化を実現します。また、アプリの自動アップデート中のシステム シャットダウンや無線(OTA)アップデート中のアプリの再コンパイルを回避することで、AOT コンパイラのパフォーマンスも改善します。

JIT と AOT は同じコンパイラを使用して同様の最適化セットを実施しますが、生成されるコードは同じとは限りません。JIT はランタイム型の情報を利用するとともに、より適切なインライン化を行い、また、オンスタック置換(OSR)コンパイルを可能にします。これらすべての結果として、わずかに異なるコードが生成されます。

JIT のアーキテクチャ

JIT のアーキテクチャ
図 1. JIT のアーキテクチャ

JIT コンパイル

JIT コンパイルは次のアクティビティで構成されます。

プロファイル ガイド付きコンパイル
図 2. プロファイル ガイド付きコンパイル
  1. ユーザーがアプリを実行すると、ART がトリガーされて .dex ファイルが読み込まれます。
    • .oat ファイル(.dex ファイルの AOT バイナリ)が使用できる場合、ART は直接それを使用します。.oat ファイルは定期的に生成されますが、コンパイル済みコード(AOT バイナリ)を含むとは限りません。
    • .oat ファイルにコンパイル済みコードが含まれていない場合は、ART が JIT とインタープリタを使用して .dex ファイルを実行します。
  2. speed コンパイル フィルタ(「できる限りアプリからコンパイルする」ことを指定します)に従ってコンパイルされていないすべてのアプリで、JIT が有効化されます。
  3. JIT プロファイル データは、アプリだけがアクセスできるシステム ディレクトリ内のファイルにダンプされます。
  4. AOT コンパイル(dex2oat)デーモンは、コンパイルを進めるためにそのファイルを解析します。

    JIT デーモン
    図 3. JIT デーモンのアクティビティ

共有ライブラリと同様に動作する別のアプリで使用される例としては、Google Play 開発者サービスがあります。

JIT のワークフロー

JIT のアーキテクチャ
図 4. JIT のデータフロー
  • プロファイリング情報はコード キャッシュに格納され、メモリ不足の状況ではガベージ コレクションの対象になります。
    • アプリがバックグラウンドで実行されたときに取得されたスナップショットに、完全なデータ(JIT コンパイルされたすべてのデータ)が含まれる保証はありません。
    • すべてのデータを記録しようとするとランタイムのパフォーマンスに影響するため、完全な記録は保証されません。
  • メソッドには次の 3 つの状態があります。
    • 解釈済み(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 

注: このコマンドを実行しても、ローカルのプロファイル データは保持されます。