Android Runtime (ART)は、Android 5.0 LollipopでDalvikの後継として導入されたAndroidアプリケーション実行環境です。主な革新は、アプリケーションインストール時にDEXバイトコードをネイティブマシンコードに事前コンパイル(AOT)することで、JITコンパイラのウォームアップという長年の問題を解消したことです。Google、2024年によると、ARTはDEX形式との完全な下位互換性を維持しながら、Dalvikと比較して20〜30%のパフォーマンス向上を実現しています。
重要ポイント
Android Runtime (ART)は、実行前にDEXバイトコードをネイティブマシンコードにコンパイルするアプリケーション実行環境です。実行時にJust-In-Timeコンパイルを使用していたDalvikとは異なり、ARTはAPKインストール時にAhead-Of-Time(AOT)コンパイルを実行します。この根本的なアーキテクチャ変更により、アプリケーションの大幅な高速化と消費電力の削減が実現しました。
ARTは当初、Android 4.4 KitKatで実験的オプションとして登場しました。開発者は開発者設定で有効にしてアプリケーションをテストできました。Android 5.0 LollipopではARTがデフォルトのランタイムとなり、Dalvikはプラットフォームから完全に削除されました。Android 7.0 Nougatのリリースまでに、ARTはハイブリッドコンパイルモードを獲得しました。
DalvikをARTに置き換える決定は突然のものではありませんでした。新しいランタイムの作業は、GoogleがJITアプローチの限界を認識した2012年に始まりました。主な目標:アプリ起動の高速化、CPU負荷の低減、消費電力の削減。開発は、以前Dalvikの最適化に取り組んでいたAndroid Runtime Groupが主導しました。
ARTはDalvikと同じレジスタベースのアーキテクチャを使用していますが、完全に再設計されたコンパイラを備えています。インタプリタとJITコンパイラの代わりに、ARTにはdex2oat AOTコンパイラが含まれており、インストール時にDEXファイルをELFバイナリに変換します。その結果、ART上のアプリケーションはウォームアップフェーズなしで即座にネイティブパフォーマンスで起動します。
ARTはDalvikの主要な原則を維持しています:個別のプロセスによるアプリケーションの分離、レジスタベースのアーキテクチャ、DEX形式のサポート。ただし、内部実装は完全に書き直されました。Dalvikインタプリタの代わりに、ARTには3つの実行モード(インタプリタ、JITコンパイラ、dex2oat AOTコンパイラ)が含まれています。モードの選択はアプリケーションのライフサイクルステージによって異なります。
ARTの主要コンポーネントはdex2oat(dalvik executable to optimized android translator)です。このユーティリティはアプリケーションインストール時に実行されます(Android 7.0以降はバックグラウンド最適化時にも実行されます)。dex2oatはAPKからDEXファイルを読み取り、バイトコードを最適化して、ネイティブコードを含むELFバイナリであるOATファイルを生成します。OATファイルは/data/dalvik-cache/ディレクトリに保存されます。
# デバイス上のOATファイルを確認する
adb shell ls -la /data/dalvik-cache/arm64/
# アプリケーションの強制的な再コンパイル
adb shell cmd package compile -m speed com.example.app
ARTシステムは、いくつかの相互接続されたモジュールで構成されています。dex2oatコンパイラはネイティブコード生成を担当します。ガベージコレクタ(GC)はメモリ解放を管理します。インタプリタはコンパイルなしで頻繁に呼び出されないコードを実行します。プロファイラはハイブリッドコンパイルのためにホットメソッドを追跡します。各モジュールは独立して動作でき、ARTを柔軟でスケーラブルにしています。
Android 7.0 Nougat以降、ARTはJITとAOTの利点を組み合わせたハイブリッドアプローチをコンパイルに使用しています。アプリケーションのインストール時に、ARTは完全なAOTコンパイルを実行しなくなりました。代わりに、アプリはホットメソッドのJITコンパイルを伴うインタプリタモードで実行されます。これにより、インストール時間とストレージ容量が削減されます。
バックグラウンドプロファイラが並行して動作します。どのメソッドが最も頻繁に呼び出されるか、どのコードブランチが実行されるか、どのクラスがロードされるかなどの実行統計を収集します。十分なデータ(通常2〜3回のアプリ起動後)が蓄積されると、ARTはバックグラウンドでdex2oatを実行し、プロファイルされたホットメソッドのみをネイティブコードにコンパイルします。
ARTは、system_serverを通じて管理されるいくつかのコンパイルモードをサポートしています。「speed」モードはすべてのメソッドをAOTでコンパイルします(最大パフォーマンス、インストールは遅い)。「speed-profile」モードはプロファイルされたホットメソッドのみをコンパイルします(速度とサイズのバランス)。「verify」モードはコンパイルなしでバイトコードのみを検証します(最小スペース、インタプリタ)。デフォルトでは、speed-profileが使用され、ほとんどのアプリケーションに最適です。
| モード | コンパイル | インストール時間 | パフォーマンス |
|---|---|---|---|
| speed | 完全AOT | 遅い | 最大 |
| speed-profile | プロファイルAOT | 速い | 高い |
| verify | コンパイルなし | 即時 | インタプリタ |
| space | 最小AOT | 中程度 | 中程度 |
プロファイラは、特別な.profファイルに実行データを収集します。各アプリケーションはそのプロファイルを/data/misc/profiles/に保存します。しきい値(通常1000サンプル)に達すると、プロファイラはdex2oatを起動して特定されたホットメソッドをコンパイルします。プロファイルはアプリケーションのアップデート間で保持され、OTAシステムアップデート後の再最適化を高速化します。
ARTのガベージコレクションはDalvikと比較して劇的に改善されました。シングルスレッドのConcurrent Mark and Sweep(CMS)の代わりに、ARTは世代別コレクタを複数の最適化とともに使用しています:移動コレクタ(ヒープの圧縮)、ラージオブジェクトスペース(大きなオブジェクト用の個別ストレージ)、および並行圧縮(並列圧縮)。
ARTの典型的なGC一時停止は2〜3ミリ秒で、Dalvikの5〜10ミリ秒と比較して大幅に短縮されました。これはいくつかのメカニズムにより可能になりました。第一に、ARTは並行フェーズでstop-the-worldの代わりにread-barrierを使用します。第二に、世代別コレクタはほとんどのサイクルでオブジェクトの若い世代のみを処理し、ヒープ全体に触れません。第三に、ラージオブジェクトスペース(LOS)は個別に割り当てられ、通常のGCサイクルには参加しません。
// デバッグ用にGCログを有効にする
System.logV("ART", "GC trigger: allocation failed");
// 強制的なGC呼び出し(本番環境では推奨されません)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Debug.getRuntimeIStats();
}
GCが改善されたにもかかわらず、メモリリークは依然として重要な問題です。ART固有の原因は、適切な解放を行わずにJNIを介してネイティブライブラリをロードすることです。ネイティブコードがmallocを介してメモリを割り当ててもfreeを呼び出さない場合、ARTはこのメモリを解放できません。これは管理ヒープの外部にあるためです。Android NDKのAddressSanitizerツールは、このようなリークの特定に役立ちます。
ARTとDalvikは、同じタスク(Androidアプリケーションの実行)に対する2つの根本的に異なる実装です。違いはコンパイルからメモリ管理まで、すべてのレベルに影響します。以下は、主要なパフォーマンスと互換性パラメータの比較です。
ARTの主な利点はJITウォームアップの排除です。Dalvikでは、JITがホットメソッドをコンパイルしている間、アプリは最初の3〜10秒間遅延する可能性がありました。ARTでは、すべてのメソッドがすでにネイティブコードにコンパイルされているか(またはバックグラウンドでコンパイルされます)。これは特にゲームや重いUIを持つアプリで顕著で、fpsの差はART側に15〜20%に達する可能性があります。
| パラメータ | Dalvik | ART |
|---|---|---|
| コンパイル | JIT(実行時) | AOT + ハイブリッド(インストール時) |
| 起動時間 | 3〜10秒(ウォームアップ) | 即時 |
| APKサイズ | 〜6〜7 MB(DEX) | +20%(OAT) |
| GC一時停止 | 5〜10ミリ秒 | 2〜3ミリ秒 |
| 消費電力 | 高い(JITがCPUを加熱) | 低い(ネイティブコード) |
Dalvik用に書かれたすべてのアプリケーションは、変更なしでARTで動作します。GoogleはDEXバイトコードレベルでの完全な下位互換性を保証しています。例外は、リフレクションを介してDalvik固有の内部APIを使用するコードです:Android SDKで@hideとマークされたdalvik.system.DexFileクラスのメンバー。そのようなコードはパブリックAPIを使用するように更新する必要があります。
ARTは、Java 8機能をネイティブサポートする最初のAndroidランタイムとなりました。Android 7.0以降、ARTにはデシュガリングが含まれています。これはJava 8の構文(ラムダ、メソッド参照、Stream API)を同等のJava 7コードに変換するプロセスです。これにより、古いデバイスとの互換性を失うことなく、最新の構文を使用できます。
デシュガリングはD8コンパイラによって実行され、次のように機能します。ラムダを含むソースコードは同じクラス内のシンセティックメソッドに変換され、ラムダはinvoke-custom呼び出しに置き換えられます。ARTのランタイムには、特にJava 8用に追加されたinvoke-custom命令のサポートが含まれています。Android 6.0以下のデバイスでは、ラムダは匿名クラスにデシュガリングされます。
// Java 8ラムダ — ARTでのデシュガリング
button.setOnClickListener(v -> handleClick(v));
// デシュガリング後(Java 7相当)
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v);
}
});
すべてのJava 8機能がデシュガリングでサポートされているわけではありません。java.time API(日付と時刻)は、build.gradleに追加される追加ライブラリであるdesugar_jdk_libsを介してのみ利用可能です。Stream APIもdesugar_jdk_libsが必要です。java.util.functionとOptionalは追加の依存関係なしで動作します。完全なJava 8サポートは、デシュガリングなしでAndroid 8.0以上のデバイスで利用可能です。
ARTは下位互換性がありますが、いくつかの最適化手法は特にこのランタイムでパフォーマンスを向上させます。主な推奨事項はリフレクションを最小限に抑えることです。ARTはコンパイル時に可視のメソッドを直接マシンコード呼び出しにコンパイルします。リフレクションはARTに追加のスタブを生成させるため、実行速度が10〜15%低下します。
Android 9.0以降、ARTはApp Startup Optimizationのサポートを導入しました。開発者はマニフェストで<initialization>を介して初期化クラスをマークでき、ARTはアプリ起動時にそれらをプリロードします。これにより、多くのプラグインやライブラリを持つアプリケーションの起動時間が5〜15%短縮されます。
<!-- AndroidManifest.xmlでのApp Startup Optimization -->
<application>
<profileable
android:shell="true"
android:enable="true" />
</application>
ARTでのパフォーマンスを測定するには、systraceとperfettoを使用します。Systraceはdex2oatのコンパイル時間、GC頻度、フレームレンダリング速度を表示します。Perfettoはより詳細な情報(スレッド分布、JNI遷移時間、ネイティブライブラリのロード)を提供します。起動:adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto -t 10s sched freq idle am wm。
よくある質問
ART(Android Runtime)は、インストール時にアプリコードをマシンコードにコンパイルするAndroidアプリケーション実行環境です。これにより、古いDalvikランタイムと比較してアプリの起動と動作が高速化されます。
ARTはアプリケーションインストール時に事前(AOT)にコードをコンパイルしますが、Dalvikは実行時に(JIT)断片的にコンパイルしていました。そのため、ARTではアプリケーションの起動が速く、消費電力も少なくなります。
adb shell getpropを実行し、persist.sys.dalvik.vm.lib.2プロパティを探します。「libart.so」の値はARTを意味し、「libdvm.so」はDalvikを意味します。Android 5.0+のすべてのデバイスはARTをランタイムとして使用しています。
最小限です。アプリケーション自体はDEXファイルを含むAPK形式のままです。ARTは/data/dalvik-cache/に追加のOATファイルを作成します。これは元のDEXより10〜20%多くの容量を占有しますが、このストレージはAPKサイズには含まれません。
はい。ARTはデシュガリングメカニズムを通じてほとんどのJava 8機能をサポートしています。ラムダ、メソッド参照、関数型インターフェースはAndroid 5.0+のすべてのデバイスで動作します。Stream APIとjava.timeにはdesugar_jdk_libsライブラリが必要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。