ビットマップは、アプリ内で最もメモリを消費するオブジェクトであることがよくあります。デコードとスケーリングのオペレーションは、フレーム レンダリングのクリティカル パス上で行われることが多くあります。 ビットマップのメモリ使用量を最適化すると、ジャンク、ANR、OOM 関連のプロセス強制終了が減り、UI の応答性、バッテリー寿命、全体的な安定性が大幅に向上します。
ビットマップのメモリ使用量が多いことを特定する
Android Vitals は、Android デバイスから集計したデータに基づいて、アプリのビットマップのメモリ使用量に関する指標を提供します。デフォルトでは、これらの指標は、日単位のデータの 28 日間のサマリーとして計算されます。このデータは、さまざまなデバイスタイプとバージョンにおけるメモリ効率の傾向と潜在的な回帰を特定するのに役立ちます。
Android Vitals は、アプリのビットマップのメモリ使用量を次の プロセス状態別に分類して共有します。
- フォアグラウンド: アプリのプロセスが表示されています。フォアグラウンドでは、他のプロセス状態と比較して P99 が大幅に高くなることが想定されますが、P99/P50 比率が有意な場合(3.5 倍を超える場合など)は、ビットマップのメモリリークを示していることが多いため、デベロッパーは調査する必要があります。これは、通常の使用量(P50)と外れ値の使用量(P99)の乖離を確認することで特定できます。一般的なアセットの肥大化は、すべてのパーセンタイルでメモリを均一に増加させますが、メモリリークは時間の経過とともに複合化され、テールエンドのデータ(P99)を大きく歪めます。アプリが他の状態に移行した後、フォアグラウンドのビットマップ割り当てが不必要に保持されないようにしてください。
- ユーザーが認識できるサービス: アプリのプロセスが 認識可能な状態で実行されています。これには、フォアグラウンド サービス、緊急 ジョブ、ユーザーが開始するデータ転送ジョブが含まれます。アプリは、これらの状態に移行する際に、大量のフォアグラウンド ビットマップ割り当てを保持してはなりません。これらのサービスは長時間実行されるタスク向けに設計されているため、大きなアセットを保持すると全体的なユーザー エクスペリエンスが低下し、Low Memory Killer Daemon(LMKD)が優先度の低いプロセスを終了してメモリを回収せざるを得なくなります。
- バックグラウンド: アプリがバックグラウンド サービスを実行しているか、最近 バックグラウンドに移行したものの、まだキャッシュに保存されていません。このプロセス状態は、フォアグラウンド プロセスや認識可能なプロセスよりも重要度が低いため、アプリはここで大きなビットマップ アセットを明示的に解放して、メモリ負荷を軽減する必要があります。
- キャッシュ: アプリがキャッシュ状態です。この状態は、LMK などのシステム メモリ負荷に非常に敏感です。アプリは、この状態でビットマップのメモリ使用量を積極的に削減して、OS による強制終了を回避する必要があります。
ビットマップのメモリ使用量が多い原因
使用されなかった仮想メモリも計算に含まれる場合があります。ビットマップのメモリ使用量が予想以上に多い場合は、未使用のメモリを割り当てていないことを確認してください。
リソース
Android Studio でビットマップを分析する
ビットマップの Android Studio プロファイリング
Memory Profiler を使用して、メモリ割り当てをリアルタイムで検査し、ヒープダンプをキャプチャして、オブジェクトのメモリリークを分析します。また、ヒープ アナライザを使用して、メモリリークの検出、重複するビットマップ割り当ての特定、オブジェクト保持の可視化を行います。
LeakCanary による自動リーク検出
LeakCanary ライブラリを統合して、アプリ内のメモリリークの検出を自動化します。LeakCanary は、自動ヒープ分析を提供し、ガベージ コレクションされるべきだがメモリに保持されているオブジェクト(破棄されたコンポーネントによって保持されているビットマップなど)を特定します。
ビットマップのパフォーマンスに関するドキュメント
これらのリソースでは、さまざまな Android コンポーネントでビットマップを効率的に処理するためのベスト プラクティスに関する包括的なガイダンスを提供しています。
ビットマップのメモリ使用量を最適化するためのデベロッパー向けチェックリスト
ビットマップのメモリ効率を最適化するには、削減、再利用、リサイクルの 3 つの基本原則に従います。
- 削減: ビットマップの読み込み時または表示時の初期メモリ使用量を最小限に抑えます。
- 再利用: キャッシュ メカニズムを実装して、冗長なビットマップ 割り当てを回避します。
- リサイクル: リソースを積極的に解放して、 アクティブなプロセスでメモリを再割り当てできるようにします。
次のデベロッパー向けチェックリストは、ビットマップのメモリ使用量を最適化するのに役立ちます。
| 基本原則 | 地域 | 説明 |
|---|---|---|
| 削減 | 重複するビットマップを削除する | Memory Profiler を使用してヒープダンプを分析し、冗長なビットマップ割り当てを検出します。ビットマップ メモリの管理ガイドを参照してください。 |
| 画像読み込みライブラリを活用する | Glide や Coil などのライブラリを使用して、スレッド処理、キャッシュ、効率的なデコードを自動化します。 | |
| ダウンサンプリングを実装する | 画像をデコードして、フル解像度のアセットを読み込むのではなく、ターゲット UI コンテナのサイズに合わせます。 | |
| 不透明な画像に RGB_565 を使用する | 透明度のない画像で ARGB_8888 から 16 ビット構成に切り替えることで、メモリ使用量を 50% 削減します。 |
|
| VectorDrawable を優先する | アイコンや基本的なグラフィックにベクターを使用することで、メモリオーバーヘッドを最小限に抑えながらシャープなスケーリングを実現します。 | |
| サーバーサイドの画像配信を最適化する | デバイスの密度と UI コンテナのサイズに合わせて画像を配信するようにバックエンド API を構成します。 | |
| 透明な余白を削除する | 組み込みの余白の代わりに InsetDrawable またはレイアウト パディングを使用することで、「見えない」ピクセルにメモリを割り当てないようにします。メモリ効率の高い Android アプリを設計するをご覧ください。 | |
| 再利用 | 最適なキャッシュ サイズを構成する | デバイスの RAM と画面解像度に基づいて、メモリとディスク キャッシュの上限を調整します。ビットマップのキャッシュ保存を参照してください。 |
| リサイクル | バックグラウンドでリソースを削除する | TRIM_MEMORY_BACKGROUND を実装して、キャッシュをクリアし、システム メモリ負荷時のプロセス生存率を向上させます。 |
| UI が非表示になったらアセットを解放する | アプリがユーザーに表示されなくなったら、TRIM_MEMORY_UI_HIDDEN を使用してビットマップ キャッシュを解放します。 |
|
| メモリリークをモニタリングする | LeakCanary と Memory Profiler を使用して、LifecycleOwner が破棄された後に保持されているビットマップを見つけます。アプリのメモリを管理するを参照してください。 |