ヒープダンプをキャプチャする

ヒープダンプをキャプチャすると、キャプチャ時にアプリのどのオブジェクトがメモリを不必要に消費しているかを確認し、メモリリークや、スタッター、フリーズ、アプリのクラッシュにつながるメモリ割り当て動作を特定できます。特に、長いユーザー セッションの後にヒープダンプを取得すると、メモリに存在しないと思われるオブジェクトが引き続きメモリにあることが示されるため、役に立ちます。

このページでは、Android Studio がヒープダンプの収集と分析のために提供するツールについて説明します。または、コマンドラインで dumpsys を使ってアプリのメモリを調べたり、 Logcat のガベージ コレクション(GC)イベントを表示したりすることもできます

アプリのメモリをプロファイリングする理由

Android は 管理メモリ 環境を備えています。Android が アプリが一部のオブジェクトを使用していないことを検出すると、ガベージ コレクターは 未使用のメモリをリリースしてヒープに戻します。Android が未使用のメモリを見つける方法は常に改善されていますが、すべての Android バージョンで、システムはコードを一時的に一時停止する必要があります。ほとんどの場合、一時停止は認識できません。 システムがメモリを収集できる以上の速度でアプリがメモリを割り当てると、コレクターが十分なメモリを解放して割り当てを満たしていても、アプリで遅延が発生する場合があります。この遅延により、アプリがフレームをスキップしたり、明らかに速度が低下する場合があります。

アプリで速度低下が発生していない場合でも、メモリリークが発生していれば、アプリはバックグラウンドにあってもメモリを保持します。これにより、不必要なガベージ コレクション イベントが強制的に実行され、システム上の残りのメモリ パフォーマンスが低下する可能性があります。最終的には、システムによりアプリプロセスが強制終了され、メモリが回収されます。その後、ユーザーがアプリに戻ったとき、アプリプロセスを完全に再起動する必要があります。

アプリのメモリ使用量を削減できるプログラミング方法については、 [アプリのメモリを管理する]メモリをご覧ください。

ヒープダンプの概要

ヒープダンプをキャプチャするには、 [Analyze Memory Usage (Heap Dump)] タスク ([Profiler: run 'app' as debuggable (complete data)] を使用)を選択してヒープ ダンプをキャプチャします。ヒープをダンプする際に Java メモリの使用量が一時的に増加する場合がありますが、 問題はありません。ヒープダンプはアプリと同じプロセスで発生し、データを収集するためにいくらかのメモリが必要になるためです。ヒープダンプをキャプチャすると、次のものが表示されます。

Android Studio Profiler のヒープダンプ ビュー。

クラスの一覧には、次の情報が表示されます。

  • 割り当て: ヒープ内の割り当ての数。
  • ネイティブ サイズ: このオブジェクト タイプで使用されるネイティブ メモリの合計量( バイト)。Android では、 Android uses native memory for some framework classes, such as Bitmap などのフレームワーク クラスにネイティブ メモリが使用されているため、ここでは Java で割り当てられたオブジェクト用のメモリが表示されます。

  • Shallow Size: このオブジェクト タイプによって使用されている Java メモリの合計量( バイト単位)。

  • 保持サイズ: このクラスのすべてのインスタンスのために保持されているメモリの合計サイズ(バイト単位)。

ヒープメニューを使用して、特定のヒープにフィルタします。

  • App heap(デフォルト): アプリがメモリを割り当てるプライマリ ヒープ。
  • Image heap: 起動時にプリロードされたクラスを含むシステム ブートイメージ。この割り当てが変化することはありません。
  • Zygote heap: Android システムでアプリプロセスがフォークされたコピー オン ライト ヒープ。

配置プルダウンを使用して、割り当ての配置方法を選択します。

  • クラス別に配置(デフォルト): クラス名に基づいてすべての割り当てをグループ化。
  • Arrange by package: パッケージ名に基づいてすべての割り当てをグループ化。

クラス プルダウンを使用して、クラスのグループにフィルタします。

  • すべてのクラス(デフォルト): ライブラリ や依存関係のクラスなど、すべてのクラスを表示します。
  • Show activity/fragment leaks: メモリリークの原因となっているクラスを表示します。
  • Show project classes: プロジェクトで定義されたクラスのみを表示します。

クラス名をクリックして [Instance] ペインを開きます。表示される各インスタンスには次のものが含まれます。

  • Depth: GC ルートから選択済みの インスタンスへの最小ホップ数。
  • Native Size: このインスタンスのネイティブ メモリ内でのサイズ。この列は Android 7.0 以降でのみ表示されます。
  • Shallow Size: このインスタンスの Java メモリ内でのサイズ。
  • Retained Size: このインスタンスがドミネートするメモリのサイズ( ドミネーター ツリーに従う)。

インスタンスをクリックすると、[Instance Details] が表示されます。これには、[Fields] と [References] が含まれます。一般的なフィールドと参照のタイプは、Java の構造化タイプ 、配列 、プリミティブ データ型 です。フィールドまたは参照を右クリックすると、関連するインスタンスまたはソースコードの行に移動します。

  • Fields: このインスタンスのすべてのフィールドを表示します。
  • [References]: [** Instance**] タブでハイライト表示されているオブジェクトへのすべての参照を表示します。
ヒープダンプ ツール ウィンドウの [Instances]、[Fields]、[References] ビュー 。

重複するビットマップを検出する

Android Studio Narwhal 4 以降では、ヒープダンプ ビューで冗長なビットマップを検出することもできます。

検出方法は次のとおりです。

  1. Android Studio で [Profiler] タブを開きます。
  2. [Heap Dump](または [Analyze Memory Usage])をクリックし、[Record] をクリックして、アプリの現在のメモリ状態のスナップショットを取得します。
  3. 分析結果で黄色の警告三角形 ⚠️ を探します。これは、Android Studio が重複するビットマップが複数回保存されていることを示すために使用するものです。
    • または、プロファイラ ヘッダーに移動して [Filter by:] を選択し、[Duplicate Bitmaps] 設定を選択します。
  4. フラグが設定されたエントリをクリックすると、[Bitmap Preview] ペインが開き、どの画像が重複しているかを確認できます。
  5. その視覚的な確認を使用して、コード内の冗長な読み込みロジックを追跡し、より優れたキャッシュ戦略を実装します。
黄色の警告三角形 ⚠️ を使用して、重複するビットマップを探します。

メモリリークを見つける

メモリリークに関連している可能性があるクラスをすばやくフィルタするには、クラス プルダウンを開き、[Show activity/fragment leaks] を選択します。Android Studio は、アプリの Activityインスタンスと Fragmentインスタンスでメモリリークが発生していると考えられるクラスを表示します。

メモリリークを手動で確認するには、クラスとインスタンスのリストを参照して、[Retained Size] が大きいオブジェクトを見つけます。次のいずれかが原因で生じたメモリリークに注目します。

  • ホストされている Compose コンポジション グラフ(ComposeView とそのサブコンポーザブルなど)をリークできる、Activity または Context への存続期間の長い参照。
  • Jetpack Compose State オブジェクト(MutableState)、状態ホルダー、または Context をキャプチャするラムダのリーク。
  • DisposableEffectonDispose ブロックでリスナーまたはオブザーバーをクリーンアップし忘れている。
  • Activity インスタンスを保持できる、 Runnableなどの非静的内部クラス。
  • 必要以上に長くオブジェクトを保持するキャッシュ。

メモリリークの可能性がある場合は、[Instance Details] の [Fields] タブと [References] タブを使用して、目的のインスタンスまたはソースコード行に移動します。

テスト用のメモリリークをトリガーする

メモリ使用量を分析するには、アプリコードに過度な負荷をかけてメモリリークを強制的に発生させる必要があります。アプリでメモリリークを発生させるには、ヒープを調査する前にしばらくの間アプリを実行するという方法があります。リークは、ヒープ内の割り当ての先頭に徐々に出現してきます。ただし、リークが少ない場合は、検出できるまで長時間アプリを実行する必要があります。

次のいずれかの方法で、メモリリークをトリガーすることも可能です。

  • さまざまなアクティビティ状態で、デバイスを縦向きから横向きに回転させてから元に戻す動作を何度か行う。デバイスを回転させると、アプリが非同期オペレーションまたは状態ホルダー内の Activity または Context への参照を保持している場合、アプリが Activity(およびそのホストされている Compose UI ツリーと 関連する状態ツリー)をリークすることがよくあります。
  • さまざまなアクティビティの状態で、自分のアプリと別のアプリを切り替える。 たとえば、ホーム画面に移動してから自分のアプリに戻ります。

ヒープダンプの記録をエクスポートおよびインポートする

プロファイラの [Past Recordings] タブからヒープダンプ ファイルを エクスポートおよびインポートできます。Android Studio は、記録を .hprof ファイルとして保存します。

または、 jhat など別の .hprof ファイル アナライザーを使用するには、.hprof ファイルを Android 形式から Java SE .hprof ファイル形式に変換する必要があります。ファイル形式を変換するには、hprof-conv ツール {android_sdk}/platform-tools/ ディレクトリにある を使用します。hprof-conv コマンドを 2 つの引数で実行します。元の .hprof ファイル名と、変換後の .hprof ファイルを書き込む場所(新しい .hprof ファイル名を含む)。次に例を示します。

hprof-conv heap-original.hprof heap-converted.hprof

参考情報