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

DroidKaigi_2022_LINEマンガAndroid メモリ改善

Sai choko
October 12, 2022

DroidKaigi_2022_LINEマンガAndroid メモリ改善

1. LINEマンガで9年運用したAndroidアプリのレガシーの問題としてOOMのメモリクラッシュ問題を真剣に向き合って解決した事例を紹介します。
2. AndroidのVMの発展過程の紹介とそのメモリ管理方法とGCの管理方法の変化を辿る。
3. 基本的なGC(Garbage collection)の考え方からAndroidのメモリ解析のための基礎知識を伝える。
4. 具体的なメモリ解析の方法を事例で紹介する。
5. メモリリークを防ぐため、メモリを改善するための注意点と実際の例を紹介する。
6. Google I/O 2022のLarge Screen重視でさらに重要になったConfiguration changeのメモリ対策

エンジニアを募集しています。
LINE Digital Frontier株式会社の採用サイトはこちら:
https://ldfcorp.com/ja/careers/

Sai choko

October 12, 2022
Tweet

Other Decks in Programming

Transcript

  1. USER BASE ※2022年9⽉時点 MAU 作品数 取引額 作家数 WEBTOON Entertainment 最新実績

    WEBTOON Entertainment が運営する代表的なサービスは 「LINEマンガ (⽇本)」「NAVER WEBTOON (韓国)」「WEBTOON (北南⽶・欧州)」「LINE WEBTOON (東南アジア)」など 8900 万 1000 億円 600 万⼈ 10 億
  2. LINEマンガで Out of memoryが全体crashの⽐率40% -> 5% OOMが40% 他 Crash 他

    Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash OOM Crash OOM crash割合<5% 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash 他 Crash OOM Crash こちらはあくまでイメージ図であります。 メモリ改善
  3. 解析⽅法 & tools • ログ • Java JVM基礎知識 • Android

    studio メモリprofiler • Android メモリ監視メソッド • Bitmap画像のメモリ計算⽅法
  4. • GC Object参照は⾊々絡んでいる。 • Depth: The shortest number of hops

    from any GC root to the selected instance. GC treeとdepth
  5. Two important JVM tuning points in the java world •

    Heap size • -Xms, -Xmx • android:largeHeap="true” • Garbage Collection(GC) • こちらはAndroid OSバージョンとともに進化 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html - jvms-2.5 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html Java memory tuning
  6. • Android 5,6,7で起こりやすい • RAM 2GB, 3GBで発⽣; 4GB以上は少ない • Android

    8以上の端末ではOOMが少ない • ⽐較的にRAMが>=3GBのものが多いのでHeapサイズも⼤きい • Android 8以上ではNative heapにbitmapを保存 • Android 8ではConcurrent compacting garbage collectorなど メモリ回収処理が最適化されている LINEマンガ OOMの発⽣のデバイス統計
  7. • どんなタイミングでOOM するかを可視化したい • totalFreeMemory が0だ とOOMが発⽣する • 要はVMのmax Memoryを

    超えるとメモリが⾜りな いのでOOM https://stackoverflow.com/a/18375641 MemoryUtil
  8. totalFreeMemoryで 残りのメモリを数値化したい private const val MEGA_BYTE = 1048576 // 1024

    * 1024 @JvmStatic fun printCurrentApplicationVMMemory() { val runtimeVersion = System.getProperty("java.vm.version") val runtime = Runtime.getRuntime() val maxMemory = runtime.maxMemory() / MEGA_BYTE val freeMemory = runtime.freeMemory() / MEGA_BYTE val totalMemory = runtime.totalMemory() / MEGA_BYTE val totalFreeMemory = maxMemory - totalMemory + freeMemory Timber.d( "#Memory CurrentApplicationVMMemory(MB) %s, %s, %s, %s, %s", " totalFreeMemory: $totalFreeMemory", " runtimeVersion: $runtimeVersion", " maxMemory: $maxMemory", " freeMemory: $freeMemory", " totalMemory: $totalMemory", ) } MemoryUtil
  9. 33 🤔2048 x 1536のjpeg画像のファイルサイズが1MBぐらいなのにAndroid bitmapサイズは12MB になる。なぜ︖ • Bimap画像サイズ(memory) > Jpeg/PNG/webP(圧縮アルゴリズム)ファイルサイズ(dis

    k) Bitmap画像: ARGB_8888⽅式 (LINEマンガでは画像のクオリティを維持するためRGB_565を採 ⽤しない) で保存する場合のBitmapメモリサイズ(標準保存⽅式) Each pixel is stored on 4 bytes Bitmap image memory size = height x width x 4byte 例: 2048 x 1536 => 2048 x 1536 x 4 = 12582912bytes ≒ 12MB 1024 x 1600 => 1024 x 1600 x 4 = 65536000bytes ≒ 6.8MB 512 x 384 => 512 x 384 x 4 = 786432bytes ≒ 0.75MB • OutOfMemory: Faild to allocate 12582912bytes…の場合は驚かないでください。12MB/4=3MBより ⼩さいファイルサイズ(普段は1MBぐらい)の画像かもしれません。 • 質問: 最新のWebPフォーマットの画像だったらBitmapメモリサイズが⼩さくなる︖ Bitmap画像のメモリサイズ計算⽅法
  10. • Picasso .resize(600, 200) • Glideのは基本的にImageViewのサイズを⾃動判別できるが必要に応じ て.override(targetWidth, targetHeight)、 CustomTarget<Drawable>(targetImageWidth, targetImageHeight)でサ

    イズを設定 画像ライブラリー使う場合: オリジナル画像をロードしない ImageView ImageView サーバーから必要以上に⼤きい画像が送られる場合がありませんか? ImageView サイズ取得
  11. 画像分割ロード 問題 • 縦⻑い画像のままだと⼆つの問題が発⽣する • GL_MAX_TEXTURE_SIZEとかCanvas: trying to draw too

    large で古い端末では描画できない可能性がある • RecyclerViewで画⾯に表⽰されていない部分がrecyclerできない 解決⽅法 • meta dataを参考に事前に分割する適切なサイズを計算する • Android 8.0以下: BitmapRegionDecoderで分割読み込み 、 読み込みスピードを犠牲 • Android 8.0以上: オリジナルBitmap読み込んでからsub bitmapに分割して表⽰、 読み込みスピード優先
  12. • fast scrollで画⾯上に表⽰する必要のない 画像はロードのcallbackが来たら処理しない • NO_POSITIONのholderの画像はもうロードする必要がない。 public void onBitmapLoaded(Bitmap bitmap,

    … int requestPosition) { …. int adapterPosition = holder. getAbsoluteAdapterPosition(); // check whether it is valid position. // - avoid calling unnecessary logic such as createBitmap. // - this also fix when fast scroll wrong position bitmap set. if (adapterPosition == RecyclerView.NO_POSITION || requestPosition != adapterPosition) { return; } …. Create bitmap … Viewer: RecyclerViewのfast scroll対応 NO_POSITION … … NO_POSITION
  13. private var binding: xxxxBinding? = null …. override fun onDestroyView()

    { super.onDestroyView() binding = null } 参照: Release Binding/Adapter
  14. • Adapter release when onDestroy • or not use field

    adapter, just cast for use adapter = null or recyclerView.setAdapter(null); (adapter as xxxxAdapter).get….. 参照: Release Adapter
  15. class xxxxFragment { private val myView: View? = null //

    Bad practice private val myDialogFragment MyDialogFragment? = null // Bad practice } • Filedに保持しない、必要な時はbinding/findFragmentByTag/byId で参照できる 参照: view/dialogを保持しない
  16. • Handler(Looper.getMainLooper()).postDelayed(…..). // Bad practice • 実際画像バナー⾃動横scrollでこれを使って無限ループで⾃⼰参照しているところがあ りました。バナー画像は⼤きいですからメモリリークされてメモリにずっと残っていま した。 •

    rootView.postDelayed(…..) // Good practice • Runnableのstop処理を⾃前で書いてもいいですがこの場合viewのpost/postDelayed が適切 • Viewに紐ついてPostDelayedするとattatch状態もちゃんとチェックしてくれます。 View.java 参照: 遅延処理はViewのpostDelayedを使う
  17. • Google I/O 2022でもLarge screen(tablet/foldable)が話題 • Large screen対応アプリはGoogle playの該当デバイス種類検索ラン キング上位に現れるなど優遇される

    • Play Media Experience Programに申請して対応すると⼿数料 が30% -> 15%になります。 • ConfigutationChange/rotationなどで発⽣する可能性が⾼いメモリ リークなど問題ないか解析が必要になっています • LINEマンガは既にLarge Screen対応済み おまけ: Google large screen対応