ヒープダンプをキャプチャすると、キャプチャ時にアプリのどのオブジェクトがメモリを不必要に消費しているかを確認し、メモリリークや、スタッター、フリーズ、アプリのクラッシュにつながるメモリ割り当て動作を特定できます。特に、長いユーザー セッションの後にヒープダンプを取得すると、メモリに存在しないと思われるオブジェクトが引き続きメモリにあることが示されるため、役に立ちます。
このページでは、Android Studio がヒープダンプの収集と分析のために提供するツールについて説明します。または、コマンドラインで
dumpsys を使ってアプリのメモリを調べたり、
Logcat のガベージ コレクション(GC)イベントを表示したりすることもできます。
アプリのメモリをプロファイリングする理由
Android は 管理メモリ 環境を備えています。Android が アプリが一部のオブジェクトを使用していないことを検出すると、ガベージ コレクターは 未使用のメモリをリリースしてヒープに戻します。Android が未使用のメモリを見つける方法は常に改善されていますが、すべての Android バージョンで、システムがコードを一時的に一時停止する必要があります。ほとんどの場合、一時停止は認識できません。 システムがメモリを収集できる以上の速度でアプリがメモリを割り当てると、コレクターが十分なメモリを解放して割り当てを満たしていても、アプリで遅延が発生する場合があります。この遅延により、アプリがフレームをスキップしたり、明らかに速度が低下する場合があります。
アプリで速度低下が発生していない場合でも、メモリリークが発生していれば、アプリはバックグラウンドにあってもメモリを保持します。これにより、不必要なガベージ コレクション イベントが強制的に実行され、システム上の残りのメモリ パフォーマンスが低下する可能性があります。最終的には、システムによりアプリプロセスが強制終了され、メモリが回収されます。その後、ユーザーがアプリに戻ったとき、アプリプロセスを完全に再起動する必要があります。
アプリのメモリ使用量を削減できるプログラミング方法については、アプリのメモリを管理するをご覧ください。
ヒープダンプの概要
ヒープダンプをキャプチャするには、 [Analyze Memory Usage (Heap Dump)] タスク ([Profiler: run 'app' as debuggable (complete data)] を使用)を選択してヒープ ダンプをキャプチャします。ヒープをダンプする際に Java メモリの使用量が一時的に増加する場合がありますが、 問題はありません。ヒープダンプはアプリと同じプロセスで発生し、データを収集するためにいくらかのメモリが必要になるためです。ヒープダンプを取得すると、次の内容が表示されます。
クラスの一覧には、次の情報が表示されます。
- 割り当て: ヒープ内の割り当ての数。
ネイティブ サイズ: このオブジェクト タイプで使用されるネイティブ メモリの合計量( バイト)。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**] タブでハイライト表示されているオブジェクトへのすべての参照を表示します。
メモリリークを見つける
メモリリークに関連している可能性があるクラスにすばやくフィルタするには、クラス プルダウンを開いて [Show activity/fragment
leaks] を選択します。Android Studio
は、アプリの
Activityインスタンスと
Fragmentインスタンスでメモリリークが発生していると考えられるクラスを表示します。
メモリリークを手動で確認するには、クラスとインスタンスのリストを参照して、[Retained Size] が大きいオブジェクトを見つけます。次のいずれかが原因で生じたメモリリークに注目します。
- ホストされている Compose
コンポジション グラフ(
ComposeViewやそのサブコンポーザブルなど)をリークする可能性がある、ActivityまたはContextへの存続期間の長い参照。 - Jetpack Compose State オブジェクト(
MutableState)、状態ホルダー、またはContextをキャプチャするラムダのリーク。 -
DisposableEffectのonDisposeブロックでリスナーまたはオブザーバーをクリーンアップし忘れている。 -
インスタンスを保持できる、
Runnableなどの非静的内部クラス。Activity - 必要以上に長くオブジェクトを保持するキャッシュ。
メモリリークの可能性がある場合は、[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 ファイル名と、変換後の .hprof
ファイルを書き込む場所(新しい .hprof ファイル名を含む)の 2 つの引数を指定して hprof-conv
コマンドを実行します。次に例を示します。
hprof-conv heap-original.hprof heap-converted.hprof