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. マンガアプリの
    メモリ改善と解析⽅法
    © LINE Digital Frontier Corporation
    2022/10/5
    Sai.choko

    View Slide

  2. • sai.choko
    • https://twitter.com/zhangho
    • 中国 -> ⽇本 -> 中国 -> ⽇本
    • LINEマンガの開発

    View Slide

  3. © LINE Digital Frontier Corporation

    View Slide

  4. USER BASE
    ※2022年9⽉時点
    MAU 作品数
    取引額 作家数
    WEBTOON Entertainment 最新実績
    WEBTOON Entertainment が運営する代表的なサービスは
    「LINEマンガ (⽇本)」「NAVER WEBTOON (韓国)」「WEBTOON (北南⽶・欧州)」「LINE WEBTOON (東南アジア)」など
    8900

    1000
    億円
    600
    万⼈
    10

    View Slide

  5. LINEマンガ
    メモリ問題の背景
    © LINE Digital Frontier Corporation

    View Slide

  6. レガシー
    2013年からアプリを9年以上運⽤したアプリ
    Java…Kotlin化
    AsyncTask…Coroutine
    アーキテクチャー…MVVMだったり
    メモリ問題…どこから着⼿すればいいのか︖

    View Slide

  7. 現在、将来
    画⾯回転
    Large Screen: Tablet, foldable
    Google Play Media Experience Program (⼿数料30% -> 15%)

    View Slide

  8. マンガViewerはメモリ⼤量消費
    縦⻑画像
    メイン
    画⾯
    マンガ
    Viewer
    Viewerでは⼗分なメモリを確保したい。

    View Slide

  9. 本資料の⽬的
    • Java/Androidメモリの基礎知識勉強
    • メモリ解析ノウハウ共有
    • Best practiceをまとめる

    View Slide

  10. 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
    こちらはあくまでイメージ図であります。
    メモリ改善

    View Slide

  11. 解析⽅法 & tools
    • ログ
    • Java JVM基礎知識
    • Android studio メモリprofiler
    • Android メモリ監視メソッド
    • Bitmap画像のメモリ計算⽅法

    View Slide

  12. 12
    Crashログ
    みなさん⼤好きなCrashログ:
    Crashログから検知 (Firebase Crashlytics、⾃社ログサービスなど)
    ログだけ頼ってメモリ問題を解決しようとしたが
    根本的な解決には繋がらなかった。
    いい点: 問題の箇所がすぐわかる
    悪い点︓表⾯的な修正に終わる可能性がある、本質的な原因に辿り着かない

    View Slide

  13. 13
    解析⽅法 & tools: ログは最後の段階しか解析できない
    量的変化 -> 質的変化
    OutOfMemory

    View Slide

  14. Java/Android
    メモリの基礎知識
    © LINE Digital Frontier Corporation

    View Slide

  15. • JVMのheapに注⽬
    Javaメモリ管理⽅法

    View Slide

  16. • StackからHeap objectへのReference(参照)ことは線を切ること
    Java stackとheap

    View Slide

  17. • GC Object参照は⾊々絡んでいる。
    • Depth: The shortest number of hops from any GC root to the selected instance.
    GC treeとdepth

    View Slide

  18. GC treeとdepth
    https://www.youtube.com/watch?v=v4kCRZ_O4Lc

    View Slide

  19. メモリサイズ
    https://www.youtube.com/watch?v=v4kCRZ_O4Lc

    View Slide

  20. 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

    View Slide

  21. • 特にAndroid 8.0でメモリ管理改善
    • Compacting garbage collectorでfragment化したメモリをまとめる
    • Bitmapはjvm heapではなく、native heapに保存される
    https://source.android.com/devices/tech/dalvik
    Android Runtime (ART)

    View Slide

  22. OOMの発⽣のデバイス

    View Slide

  23. • 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の発⽣のデバイス統計

    View Slide

  24. メモリ解析ツール
    © LINE Digital Frontier Corporation

    View Slide

  25. 平坦な下がりグラフにも注意 – メモリGCできていない可能性。
    Android studio Profiling – グラフ

    View Slide

  26. ALLOCATIONS/NATIVE SIZE/SHALLOW SIZE/RETAINED SIZE /
    DEPTHの確認(GC ROOTからの距離)
    Android studio Profiling – グラフ

    View Slide

  27. Android Studio profilerの詳しい使い⽅はこちら:
    https://www.youtube.com/watch?v=v4kCRZ_O4Lc
    https://developer.android.com/studio/profile/memory-
    profiler
    デモ

    View Slide

  28. • どんなタイミングでOOM
    するかを可視化したい
    • totalFreeMemory が0だ
    とOOMが発⽣する
    • 要はVMのmax Memoryを
    超えるとメモリが⾜りな
    いのでOOM
    https://stackoverflow.com/a/18375641
    MemoryUtil

    View Slide

  29. 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

    View Slide

  30. https://square.github.io/leakcanary/
    Leak canary
    メモリリークの監視ツール

    View Slide

  31. 具体例
    © LINE Digital Frontier Corporation

    View Slide

  32. © LINE Digital Frontier Corporation
    ⼤きい画像はロードしない
    Do not load large images
    画像サイズ調整

    View Slide

  33. 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画像のメモリサイズ計算⽅法

    View Slide

  34. • Picasso .resize(600, 200)
    • Glideのは基本的にImageViewのサイズを⾃動判別できるが必要に応じ
    て.override(targetWidth, targetHeight)、
    CustomTarget(targetImageWidth, targetImageHeight)でサ
    イズを設定
    画像ライブラリー使う場合: オリジナル画像をロードしない
    ImageView
    ImageView
    サーバーから必要以上に⼤きい画像が送られる場合がありませんか?
    ImageView
    サイズ取得

    View Slide

  35. • Bitmapを直接弄る時にメモリにロードする時のサイズを調整:
    • 画像のロードサイズを画⾯サイズに合わせる
    • option.inSampleSize = 2 // 2の倍数
    • https://developer.android.com/topic/performance/graphics/load
    -bitmap
    画像サイズ⼿動調整: inSampleSize

    View Slide

  36. 画像分割ロード
    問題
    • 縦⻑い画像のままだと⼆つの問題が発⽣する
    • GL_MAX_TEXTURE_SIZEとかCanvas: trying to draw too large
    で古い端末では描画できない可能性がある
    • RecyclerViewで画⾯に表⽰されていない部分がrecyclerできない
    解決⽅法
    • meta dataを参考に事前に分割する適切なサイズを計算する
    • Android 8.0以下: BitmapRegionDecoderで分割読み込み 、
    読み込みスピードを犠牲
    • Android 8.0以上: オリジナルBitmap読み込んでからsub bitmapに分割して表⽰、
    読み込みスピード優先

    View Slide

  37. ⼀気に多い画像をロードしない
    Don't load too many images at once
    画像ロード量調整

    View Slide

  38. マンガViewer、メモリSpike問題

    View Slide

  39. • 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

    View Slide

  40. • low end deviceʼs recyclerview cache limit
    Viewer: RecyclerViewのviewcache制限

    View Slide

  41. 無駄なメモリ参照を無くそう もしくは 範囲を狭める
    Remove useless memory references OR Narrow the scope
    メモリ参照をなくす

    View Slide

  42. • Memcacheがずっとメモリを占⽤しているのを防ぎましょう。
    • Picasso⼿動でclearする必要あります。しないとmemcacheで15%のheapメ
    モリが使えなくなります => Picasso.cache.clear()
    • Glideなどはactivity/viewなどのcontextにより⾃動メモリ解放しているらしい
    が必要に応じてclearしておく => Glide.get(context).clearMemory()
    Memcache: Strong referenceが必要ないときにclearする

    View Slide

  43. • addObserver/removeObserve
    • addListener/removeListener
    など…..
    メモリリークはこの系でよく発⽣します。
    * 最近のLifecycler系のaddObserverとかはremoveObserverしなくても参照を残さないらしいで
    す。
    参照: addXXXXしたものは必ずremoveXXXX

    View Slide

  44. private var binding: xxxxBinding? = null
    ….
    override fun onDestroyView() {
    super.onDestroyView()
    binding = null
    }
    参照: Release Binding/Adapter

    View Slide

  45. • 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

    View Slide

  46. class xxxxFragment {
    private val myView: View? = null // Bad practice
    private val myDialogFragment MyDialogFragment? = null // Bad practice
    }
    • Filedに保持しない、必要な時はbinding/findFragmentByTag/byId で参照できる
    参照: view/dialogを保持しない

    View Slide

  47. • Handler(Looper.getMainLooper()).postDelayed(…..). // Bad practice
    • 実際画像バナー⾃動横scrollでこれを使って無限ループで⾃⼰参照しているところがあ
    りました。バナー画像は⼤きいですからメモリリークされてメモリにずっと残っていま
    した。
    • rootView.postDelayed(…..) // Good practice
    • Runnableのstop処理を⾃前で書いてもいいですがこの場合viewのpost/postDelayed
    が適切
    • Viewに紐ついてPostDelayedするとattatch状態もちゃんとチェックしてくれます。
    View.java
    参照: 遅延処理はViewのpostDelayedを使う

    View Slide

  48. 広告 sdkなどの実装が悪くでメモリリークが発⽣する可能性
    Third party sdk にも注意を払おう

    View Slide

  49. https://pszklarska.medium.com/catch-leak-if-you-can-608a99537d8a
    https://proandroiddev.com/everything-you-need-to-know-about-
    memory-leaks-in-android-d7a59faaf46a
    役に⽴つメモリ関連基礎知識

    View Slide

  50. • Flyweight or factory method で⼤きいobjectを使い回す
    • https://refactoring.guru/design-patterns/flyweight
    • Singletonもしくはfactory methodでobjectを⼀元管理
    おまけ: デザインパターン

    View Slide

  51. • 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対応

    View Slide

  52. THANK YOU !
    Sai.choko
    © LINE Digital Frontier Corporation

    View Slide