ヒープダンプをキャプチャして、キャプチャ時にアプリ内のどのオブジェクトがメモリを消費しているかを確認し、メモリリークや、スタッター、フリーズ、さらにはアプリのクラッシュにつながるメモリ割り当て動作を特定します。特に ユーザー セッションの延長後にヒープダンプを取得しているにもかかわらず、 メモリ内に存在するはずなのに
このページでは、Android Studio が提供するヒープダンプの収集と分析を行うためのツールについて説明します。または、コマンドラインで dumpsys
を使ってアプリのメモリを調べたり、Logcat でガベージ コレクション(GC)イベントを表示したりすることもできます。
アプリのメモリをプロファイリングする理由
Android は管理メモリ環境を備えています。アプリが一部のオブジェクトを使用していないことが検出されると、ガベージ コレクターは未使用のメモリをリリースしてヒープに戻します。Android が未使用のメモリを検出する仕組みは次のとおりです。 継続的に改善されていますが すべての Android バージョンで 一時停止する必要がありますほとんどの場合、その一時停止は検知できません。 ただし、システムがメモリを収集できる以上の速度でアプリがメモリを割り当てると、 コレクタが要求を満たすのに十分なメモリを解放している間、アプリで遅延が発生する可能性があります。 見ていきますこの遅延により、アプリがフレームをスキップしたり、明らかに速度が低下したりする場合があります。
アプリで速度低下が発生していない場合でも、メモリリークが発生していれば、アプリはバックグラウンドにあってもメモリを保持します。これにより、不必要なガベージ コレクション イベントが強制的に実行され、システム上の残りのメモリ パフォーマンスが低下する可能性があります。最終的には、システムによりアプリプロセスが強制終了され、メモリが回収されます。その後、ユーザーがアプリに戻ったとき、アプリのプロセスを完全に再起動する必要があります。
アプリのメモリ使用量を削減できるプログラミング方法については、アプリのメモリを管理するをご覧ください。
ヒープダンプの概要
ヒープダンプをキャプチャするには、[Analyze Memory Usage (Heap Dump)] タスクを選択します([Profiler: run 'app' as debuggable (complete data)] を使用してヒープダンプをキャプチャします)。ヒープをダンプする際に Java メモリの使用量が一時的に増加する場合があります。これは正常な動作です。ヒープダンプは、 データを収集するためにある程度のメモリが必要になります。ヒープダンプをキャプチャすると、次のように表示されます。
クラスのリストには、次の情報が表示されます。
- Allocations: ヒープ内の割り当ての数。
Native Size: このオブジェクト タイプで使用されるネイティブ メモリの合計量( 。Android では、
Bitmap
などのフレームワーク クラスにネイティブ メモリが使用されているため、ここでは Java で割り当てられたオブジェクト用のメモリが表示されます。Shallow Size: このオブジェクト タイプで使用される Java メモリの合計量( 。
Retained Size: すべてのインスタンスによって保持されているメモリの合計サイズ (バイト単位)。
[ヒープ] メニューを使用して、特定のヒープにフィルタします。
- App heap(デフォルト): アプリがメモリを割り当てるプライマリ ヒープ。
- イメージ ヒープ: 起動時にプリロードされたクラスを含むシステム ブートイメージ。この割り当てが変化することはありません。
- Zygote heap: Android システムでアプリプロセスがフォークされたコピー オン ライト ヒープ。
[配置] プルダウンを使用して、割り当ての配置方法を選択します。
- Arrange by class(デフォルト): クラス名に基づいてすべての割り当てをグループ化します。
- Arrange by package: パッケージ名に基づいてすべての割り当てをグループ化。
クラスのプルダウンを使用して、クラスのグループにフィルタします。
- すべてのクラス(デフォルト): ライブラリや依存関係のクラスを含むすべてのクラスが表示されます。
- アクティビティ/フラグメント リークを表示: メモリリークの原因となっているクラスを表示します。
- プロジェクト クラスを表示: プロジェクトで定義されたクラスのみを表示します。
クラス名をクリックして [インスタンス] ペインを開きます。各インスタンスには次のものが含まれます。 次のとおりです。
- Depth: GC ルートから選択済みのインスタンスへの最小ホップ数。
- Native Size: このインスタンスのネイティブ メモリ内でのサイズ。この列は Android 7.0 以降でのみ表示されます。
- Shallow Size: このインスタンスの Java メモリ内でのサイズ。
- Retained Size: このインスタンスがドミネートするメモリのサイズ(ドミネーター ツリーに従う)。
インスタンスをクリックして、[インスタンスの詳細] とフィールドを表示する
および参照をご覧ください。共通フィールド型と参照型は構造化型
、
配列
,
プリミティブデータ型です
説明します。フィールドまたは参照を右クリックすると、ソースコード内の関連するインスタンスまたは行に移動します。
- フィールド: このインスタンス内のすべてのフィールドが表示されます。
- References: [インスタンス] タブ。
メモリリークを見つける
メモリリークに関連している可能性のあるクラスに簡単にフィルタするには、クラスのプルダウンを開き、[アクティビティ / フラグメントのリークを表示] を選択します。Android Studio
リソースのメモリリークが発生していると考えられるクラスを、
Activity
、
アプリの Fragment
インスタンス。タイプ
次のデータが表示されます。
- 破棄されているが、まだ参照されている
Activity
インスタンス。 - 有効な
FragmentManager
がないが、まだ参照されているFragment
インスタンス。
以下のフィルタでは偽陽性が発生する可能性があります。 次のような状況になります。
Fragment
が作成されたが、まだ使用されていない。Fragment
がキャッシュされているが、FragmentTransaction
の一部ではない。
メモリリークを手動で確認するには、クラスとインスタンスのリストを参照して Retained Size が大きいオブジェクトを検出する。なんらかの理由で生じるメモリリークを 次のことができます。
Activity
への長期的参照。Context
,View
、Drawable
、その他のオブジェクトActivity
またはContext
コンテナへの参照を保持している場合があります。Activity
インスタンスを保持できる、Runnable
などの非静的内部クラス。- 必要以上に長くオブジェクトを保持するキャッシュ。
潜在的なメモリリークを見つけるには、[フィールド] タブと [参照] タブを使用します。 [Instance Details] で、対象のインスタンスまたはソースコードの行に移動します。
テスト用にメモリリークをトリガーする
メモリ使用量を分析するには、アプリコードに過度な負荷をかけてメモリの強制使用を試みる必要があります。 防ぐことができます。アプリでメモリリークを発生させるには、ヒープを調査する前にしばらくの間アプリを実行するという方法があります。リークは、ヒープ内の割り当ての先頭に徐々に出現してきます。ただし、リークが少ない場合は、アプリを長時間実行する必要があります。 する必要があります。
次のいずれかの方法で、メモリリークをトリガーすることも可能です。
- デバイスを縦向きから横向きに回転させてから元に戻す動作を何度か行う
さまざまなアクティビティ状態を維持できます。デバイスを回転させると、
Activity
、Context
、またはView
オブジェクトのリークがアプリで発生することがよくあります。これは、システムによりActivity
が再作成され、これらのいずれかのオブジェクトへの参照がどこか別の場所で保持されている場合、システムはこのようなオブジェクトをガベージ コレクションの対象にできないためです。 - さまざまなアクティビティ状態で、自分のアプリと別のアプリを切り替えます。たとえば、ホーム画面に移動してからアプリに戻ります。
ヒープダンプ レコードのエクスポートとインポート
ヒープダンプ ファイルは、プロファイラの [過去の録画] タブからエクスポートとインポートできます。Android Studio は、
.hprof
ファイルとしてエクスポートします。
jhat など別の .hprof
ファイル アナライザーを使用する場合は、.hprof
ファイルを Android 形式から Java SE .hprof
ファイル形式に変換する必要があります。ファイル形式を変換するには、{android_sdk}/platform-tools/
ディレクトリで提供されている hprof-conv
ツールを使用します。hprof-conv
を実行する
コマンドに 2 つの引数(元の .hprof
ファイル名とコピー元のファイル名)を
新しい .hprof
ファイル名を含む、変換された .hprof
ファイルを書き込みます。次に例を示します。
hprof-conv heap-original.hprof heap-converted.hprof