Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Androidバイトコード実行環境 の歴史をサクッと振り返る

Androidバイトコード実行環境 の歴史をサクッと振り返る

Androidバイトコード実行環境の歴史をサクッと振り返る
2021/03/26 potatotipsでの登壇資料
https://potatotips.connpass.com/event/202810/

782be973d3c99ed9a805886fa6c2acdf?s=128

nichiyoshi

March 26, 2021
Tweet

Transcript

  1. Androidバイトコード実行環境 の歴史をサクッと振り返る ただのインタプリタからハイブリッドコンパイル方式に至るまで をざっくり理解する

  2. 自己紹介 • 名前:Nichika Yoshida @nichiyoshi • 所属:FiNC Technologies • 職種:Androidエンジニア

    兼 スクラムマスター
  3. 唯一の特徴...

  4. 最初に期待値調整 • 以下の話はしません ◦ 最新の知見 ◦ 実装的な話 ◦ アフリカあるある

  5. 今日話すこと • Android初期 ~ 現在までの、バイトコード実行環境周辺の変遷 ◦ なんの課題を解決するために、どんなことをしたのか

  6. 今日のゴール • Androidバイトコード実行環境の変遷についてざっくり理解する • 現時点でのARTのフローをざっくり理解する

  7. 今日話さないこと • バイトコード実行環境ってなんぞや • Dalvikバイトコードやdexファイルってなんぞや • Zygoteを使ったプロセス間のメモリ共有 (補足に入れます) • mmap

    , clean/dirtyなメモリ (補足に入れます) などなど 社内LTで12分かかったので、かなり駆け足で進むと思います 補足資料もあるので興味ある方はあとで読んでください
  8. スキップ予定:dexファイルとDalvik VM • apkとdexファイル ◦ Java/KotlinのコードをJavaバイトコード(.class)に変換 ◦ .classをDalvikバイトコードに変換し、 dexファイル(.dex)にひとまとめ ◦

    dexファイルやリソースなどをまとめて apkにしてGoogle Playにup • このDalvikバイトコードを実行する環境がDalvik VM ◦ JVM同様、メモリ配置、ガベッジコレクションなども兼ね備える .java .class .kt .class .kt .class .class classes.dex javac/kotlinc APK classes.dex Resource
  9. スキップ予定:dexとDalvik VMの利点 • dexファイルの利点 ◦ 複数の.classで共通している文字列 (どのクラスでも使うimport宣言など)を重複管理せず共有する ことでファイルサイズをかなり小さく することができる •

    Dalvik VMの利点 ◦ JVMはスタック型仮装マシンで、 Dalvik VMはレジスタ型仮想マシン ▪ レジスタ型マシンの方がインタプリタでの 実行速度が速く、生成されるバイナリが小さい • dexファイルとDalvik VMは、ストレージやメモリに制限があり、安定してサクサク動 くことが重要なAndroidアプリに適している
  10. スキップ予定:クラスファイルとdexファイルに関する話をもうちょい • classファイルよりdexファイルの方がバイトコードがコンパクトだし可読性が良い ◦ ファイルサイズの違い:dexファイルは複数のクラスを一つのバイナリファイルにまとめる ことができ、複数ファイ ルで共通して記述されている文字列 (パッケージ名、クラス名、型名、メソッド名など )を共通化してidとして間接 参照できるようにすることで、同じ文字列の重複を防ぎ、

    ファイルサイズをかなり小さくできる 。 • dexは再配置なしでメモリにmmapして実行できる ◦ メモリ上にそのファイルがあるように見えるが、実際にはファイルにアクセスされるまでメモリにはロードしない。 アクセスされるまでメモリロードしないのでメモリ節約できる。 Linuxが、不要な時には破棄してくれるし、再度ア クセスしたら自動で読み込んでくれる 。 • dexは無圧縮 ◦ 圧縮しているとメモリ上で解凍が必要になるし、 dirtyなのでmmapできず、Linuxによるメモリ自動破棄ができな くなる • dexにはメソッドIDの64K問題(id制限が0xffffで65536通り, 64*2^10通り)がある => multi-dexで解決 (classes1.dex, classes2.dex, ….)
  11. スキップ予定: なんでそんなにメモリにシビアなの • Android初期はめっちゃメモリ小さかったし、今も限界がある • SDカードなどAndroidで使われる二次メモリは書き込み制限があるのでスワップができないようになって いて、メモリ情報のディスクへの一時退避ができない

  12. スキップ予定: メモリ節約の工夫 • なるべく多くのメモリをアプリ同士で共有する ◦ Androidでは、アプリのプロセスは Zygoteというプロセスをforkする。Zygote プロセスは、システム 起動時に開始され、共通のフレームワークのコードとリソース(アクティビティ テーマなど)を読み込

    む。新しいプロセスを開始するときは、システムが Zygote プロセスを fork してから、新しいプロセ スでアプリのコードをロードして実行する。 この方法により、フレームワークのコードとリソースに割 り当てられた RAM ページのほとんどを、すべてのアプリプロセスで共有 できる。 ◦ 都度プロセスを起動して ARTを初期化してクラスライブラリを初期化して ...のような処理が不要にな るので起動時間も早くなる • なるべく多くのメモリを、mmapできるCleanな状態に保つ ◦ 通常のVMだと、バイナリをメモリ上に読み込んでアドレス解決などして実行する => バイナリとメモ リ上のデータが一致しない => Dirtyなメモリ ◦ Androidでは、実行バイナリを mmapというLinuxのAPIを用いてそのまま使おうとする => Cleanなメ モリ => Linuxが、不要な時には破棄してくれるし、再度アクセスしたら自動で読み込んでくれる 。ア クセスされるまでメモリロードしないのでメモリ節約できる。
  13. とりあえず前提でこれだけ知っといて • Androidでは、JavaバイトコードでなくてDalvikバイトコードを動かすよ • 実行環境はJVMじゃなくて、ART(旧Dalvik VM)っていうAndroid用の実行環境だよ

  14. こっからようやく本題

  15. Android初期~2.1 | Dalvik VM • Dalvik VMがDalvikバイトコードを逐次解釈で実行していく素朴なインタプリタ • 課題:逐次実行なのでCPU負荷が高いような処理が遅くなりがちだった app

    app execution interpret input to 補足:初期のAndroidはメモリが今以上に少なく、 JITコンパイルなどをしてバイナリコードを生成するとメモリを逼迫してしまう。ちょっとでもメモリを節約 するために純粋なインタプリタにしたらしい。
  16. Android 2.2~4.4 | Dalvik VM + JIT • インタープリタで実行していく過程で、特にパフォーマンスに影響しそうなすごく重要 なコードのみを最低限JIT

    (Just In Time)でコンパイルし、次に同じ処理が呼ばれた 時にはこのバイナリをCPUが直接実行することで、CPU負荷が高いような処理の 実行速度の高速化 • 課題:アプリ実行中に一瞬JITが走りカクついてパフォーマンス低下 app app execution interpret/JIT input to 補足:JITで生成されたバイナリは mmapできなくてメモリ効率が悪いので、あくまで一部に限定。あとはバッテリー消費にも影響与える
  17. Android5.0 ~ 6.x | Android RunTime(ART) + AOTコンパイル • アプリのインストール時にバイトコードを全部まとめてバイナリにコンパイル

    • アプリ起動時にはバイナリをCPUが直接実行するので高速 • 課題:システムアップデートの都度、全てのアプリをAOTコンパイルするので、シス テムアップデートにめちゃくちゃ時間かかる、30分とか。 補足:他にも、アプリ起動中にバックグラウンドで他のアプリを更新中にパフォーマンス下がったり、あんま使ってないアプリやコードもバイナリ化さ れてメモリが無駄になったりする app app execution input to install & AOTコンパイル
  18. None
  19. Android7.0(N)~ | ART + JIT + profile guidedコンパイル • タイミングを分けて部分的にバイナリにコンパイル

    ◦ アプリ起動&実行中にJITで重要な部分のみバイナリ化 ◦ JITの時によく実行されるメソッドや使われるライブラリをプロファイル し、プロファイル結果で重要な 部分のみを、端末が充電中かつアイドル時に、 1日1回のみバイナリ化(Profile Guided コンパイル) • 各ユーザーのアプリ利用に合わせて最適化できる
  20. Android8.0(O) • 同時圧縮ガベージ コレクタ ◦ Android 7.0 と比較して、ヒープサイズは平均で 32% 小さく

    • Dexlayout ◦ まとめてアクセスされることが多い dex ファイルの構成要素をグループ化することで、プログラムの局所性を改善してメモリアクセス パターンを効率化し、 RAM の節約と起動時間の短縮を実現 • など様々な改善があるが、JIT + Profile Guided Compileの仕組みは変わらず
  21. Android9.0(P)~ | ART + AOT +JIT + profile guidedコンパイル •

    Cloud プロファイル ◦ JITで作成されたProfileデータが、クラウドにアップロード される ◦ 様々なユーザーのProfileを集約・解析し、何度も使われているコードは AOTコンパイルされる
  22. ARTの全体の流れ アプリ実行

  23. ARTの全体の流れ Dalvikバイトコードをインタープリタ が逐次実行しながら、 重要な箇所はJITコンパイルしてバ イナリをキャッシュし、次回は高速に 実行

  24. ARTの全体の流れ アプリ実行中、よく使われる処理は プロファイルに保存 Android9(P)以降だとプロファイルは クラウドにもアップロードされる

  25. ARTの全体の流れ アプリが充電中かつアイドルの時 に、プロファイル結果に基づいてよく 使われるコードをバイナリにコンパイ ル

  26. ARTの全体の流れ 元のバイトコードの一 部をコンパイルされた コードでreplace

  27. まとめ • Androidのバイトコード開発環境は、メモリや実行速度の戦いで常に進 化してきた • 今の形式:ART + AOT +JIT +

    profile guidedコンパイル
  28. 今日のゴールは達成できましたか? • Androidバイトコード実行環境の変遷についてざっくり理解する • 現時点でのARTのフローをざっくり理解する https://source.android.com/devices/tech/dalvik/jit-compiler

  29. 参考資料 • 記事 ◦ ART ジャストインタイム(JIT)コンパイラの実装 ◦ クラウド ART 最適化プロファイルによるアプリのパフォーマンス改善

    ◦ バージョンごとのARTに関する箇所 ▪ Android7 プロファイルに基づいた JIT / AOT コンパイル ▪ Android8 ART の改善点 ▪ Android9 ART での DEX ファイルの Ahead-of-time 変換 ▪ Android10 ART の最適化 • YOUTUBE ◦ The Evolution of ART - Google I/O 2016 ◦ LAS16-201: ART JIT Android N • BOOK ◦ Androidを支える技術〈Ⅰ〉──60fpsを達成するモダンな GUIシステム
  30. None