Skip to content

Most visited

Recently visited

navigation

Memory Profiler を使用して Java ヒープとメモリ割り当てを表示する

Memory Profiler は Android Profiler のコンポーネントであり、ぎこちない動き、フリーズ、さらにはアプリのクラッシュを引き起こす可能性があるメモリリークやメモリチャーンを特定するのに役立ちます。 このコンポーネントでは、アプリのメモリ使用量のリアルタイム グラフが表示され、ヒープダンプの取得、ガベージ コレクションの強制実行、メモリ割り当ての追跡が可能です。

Memory Profiler を開くには、次の手順を実行します。

  1. [View] > [Tool Windows] > [Android Profiler] をクリックします(ツールバーの [Android Profiler] をクリックすることもできます)。
  2. Android Profiler のツールバーからプロファイリングする端末とアプリプロセスを選択します。 USB 経由で端末に接続しているが、端末がリストに表示されない場合は、USB デバッグを有効にしていることを確認します。
  3. [MEMORY] タイムラインの任意の場所をクリックして Memory Profiler を開きます。

または、コマンドラインで dumpsys を使ってアプリのメモリを調べたり、logcat の GC イベントを表示したりできます。

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

Android は管理メモリ環境を備えています。アプリが一部のオブジェクトを使用していないことが検出されると、ガベージ コレクターは未使用のメモリをリリースしてヒープに戻します。 Android が未使用のメモリを検出する仕組みは継続的に改善されていますが、すべての Android バージョンにおいて、ある時点でコードの実行を短時間一時停止する必要があります。 ほとんどの場合、その一時停止は知覚できないものです。 ただし、アプリによって、システムがメモリを収集できる以上の速度でメモリが割り当てられると、コレクターが十分なメモリを解放して割り当てを満たしているにもかかわらず、アプリで遅延が発生する場合があります。 この遅延により、アプリがフレームをスキップしたり、明らかに速度が低下する場合があります。

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

こうした問題を防ぐには、Memory Profiler を使用して、次の措置を講じる必要があります。

アプリのメモリ使用量を削減できるプログラミング上のベスト プラクティスについては、アプリのメモリの管理をご覧ください。

Memory Profiler の概要

Memory Profiler を最初に開くと、アプリのメモリ使用量の詳細なタイムラインと、ガベージ コレクションの強制実行、ヒープダンプの取得、メモリ割り当ての記録を行えるアクセスツールが表示されます。

図 1. Memory Profiler

図 1 に示すように、Memory Profiler のデフォルト ビューには次の項目が含まれます。

  1. ガベージ コレクション イベントを強制実行するボタン。
  2. ヒープダンプを取得するボタン。
  3. メモリ割り当てを記録するボタン。 このボタンは、Android 7.1 以前を実行している端末を接続した場合にのみ表示されます。
  4. タイムラインを拡大 / 縮小するボタン。
  5. ライブメモリ データにジャンプするボタン。
  6. activity の状態、ユーザー入力イベント、画面の回転イベントを示すイベント タイムライン。
  7. 次の要素が含まれるメモリ使用量タイムライン。
    • 各メモリカテゴリによって使用されているメモリ使用量の積み重ねグラフ。メモリ使用量は左の Y 軸で、メモリカテゴリは上部の色キーで示されています。
    • 破線は割り当てられたオブジェクトの数を表しています。オブジェクトの数は右の Y 軸で示されています。
    • 各ガベージ コレクション イベントのアイコン。

ただし、Android 7.1 以前を実行している端末を使用している場合は、すべてのプロファイリング データがデフォルトで表示されるわけではありません。 「Advanced profiling is unavailable for the selected process」というメッセージが表示された場合は、次の要素を表示するために、詳細なプロファイリングを有効にする必要があります。

Android 8.0 以降では、デバッグ可能なアプリに対して詳細なプロファイリングが常に有効になります。

メモリをカウントする方法

Memory Profiler の上部に表示される数値(図 2)は、Android システムに応じて、アプリがコミットしたすべてのプライベート メモリ ページに基づいています。 このカウントには、システムまたは他のアプリが共有するページは含まれません。

図 2. Memory Profiler の上部にあるメモリカウントの凡例

メモリカウントのカテゴリは次のとおりです。

以前の Android Monitor ツールのメモリカウントと比較すると、新しい Memory Profiler はメモリを別の方法で記録するため、メモリ使用量が多くなったように見える場合があります。 Memory Profiler は、合計数を増加させることになるいくつかの追加カテゴリを監視しますが、Java ヒープメモリのみに関しては、「Java」の数値は以前のツールの値と同じようになるはずです。

また、おそらく Java の数値は Android Monitor での値と正確に一致しませんが、アプリの Java ヒープは Zygote からフォークされたため、この新しい数値には、このヒープに割り当てられたすべての物理メモリページがカウントされています。 したがって、この数値は、アプリが実際に使用している物理メモリの量を正確に表しています。

注: 現在、Memory Profiler は、アプリの誤検知されたネイティブ メモリ使用量(実際にはプロファイリング ツールによるメモリ使用量)も表示します。 最大 10 万個のオブジェクトに最大 10MB のメモリが追加されます。 このツールの将来のバージョンでは、これらの数値はデータから除外されます。

メモリ割り当てを表示する

メモリ割り当ては、メモリの各オブジェクトがどのように割り当てられたかを示します。 特に、Memory Profiler はオブジェクト割り当てに関して次の内容を表示できます。

端末が Android 8.0 以降を実行している場合は、次のようにして、いつでもオブジェクト割り当てを表示できます。 タイムラインをクリックしたままドラッグして、割り当てを表示する領域を選択します(動画 1 を参照)。 Android 8.0 以降には、アプリの割り当てを常に追跡する端末プロファイリング ツールが含まれているため、記録セッションを開始する必要はありません。

動画 1. Android 8.0 以降で、タイムラインの既存の領域を選択してオブジェクト割り当てを表示します。

端末が Android 7.1 以前を実行している場合は、Memory Profiler のツールバーで [Record memory allocations] をクリックします。 Android Monitor は記録中にアプリで発生するすべての割り当てを追跡します。 記録が完了したら、[Stop recording] (同じボタン。動画 2 を参照)をクリックして、割り当てを表示します。

動画 2. Android 7.1 以前では、メモリ割り当てを明示的に記録する必要があります。

タイムラインの領域を選択したら(または、Android 7.1 以前を実行している端末を使用して記録セッションを完了したら)、クラス名でグループ化され、ヒープカウントの順に並べられた割り当て済みのオブジェクトがタイムラインの下に表示されます。

注: Android 7.1 以前では、最大 65535 個の割り当てを記録できます。 記録セッションがこの制限を超えると、最新の 65535 個の割り当てのみが記録に保存されます (Android 8.0 以降では、事実上の制限はありません)。

割り当て記録を検査するには、次の手順を実行します。

  1. リストに目をとおして、ヒープカウントが著しく大きい、リークが発生している可能性があるオブジェクトを見つけます。 既知のクラスを見つけやすくするには、[Class Name] 列の見出しをクリックして、アルファベット順に並べ替えます。 その後、クラス名をクリックします。 図 3 のように、右側に [Instance View] ペインが開き、そのクラスの各インスタンスが表示されます。
  2. [Instance View] ペインで、インスタンスをクリックします。 ペインの下に [Call Stack] タブが表示され、そのインスタンスが割り当てられた場所と含まれるスレッドが示されます。
  3. [Call Stack] タブで任意の行をクリックして、エディタにそのコードを表示します。

図 3. 割り当てられた各オブジェクトの詳細は、右側の [Instance View] ペインに表示されます。

デフォルトでは、左側の割り当てリストはクラス名別に並べられます。 リスト上部の右側にあるプルダウンを使用して、次の並べ方に切り替えることができます。

ヒープダンプを取得する

ヒープダンプは、ヒープダンプの取得時にアプリのどのオブジェクトがメモリを使用しているかを示します。 特に、長いユーザー セッションの後、ヒープダンプにより、メモリに存在しないと思われるオブジェクトが引き続きメモリにあることが示されるため、メモリリークの特定が容易になります。 ヒープダンプを取得した後、次の内容を表示できます。

図 4. ヒープダンプの表示

ヒープダンプを取得するには、Memory Profiler のツールバーで [Dump Java heap] をクリックします。 ヒープをダンプする際に、Java メモリの使用量が一時的に増加する場合があります。 これは通常の現象です。ヒープダンプはアプリと同じプロセスで発生し、データを収集するためにいくらかのメモリを要求するからです。

図 4 のように、ヒープダンプはメモリ タイムラインの下に表示され、ヒープのすべてのクラスタイプを示します。

注: より厳密なタイミングでダンプを作成したい場合は、アプリコード内の重要な箇所で dumpHprofData() を呼び出してヒープダンプを作成することができます。

ヒープを検査するには、次の手順を実行します。

  1. リストに目をとおして、ヒープカウントが著しく大きい、リークが発生している可能性があるオブジェクトを見つけます。 既知のクラスを見つけやすくするには、[Class Name] 列の見出しをクリックして、アルファベット順に並べ替えます。 その後、クラス名をクリックします。 図 5 のように、右側に [Instance View] ペインが開き、そのクラスの各インスタンスが表示されます。
  2. [Instance View] ペインで、インスタンスをクリックします。 ペインの下に [References] タブが表示され、そのオブジェクトへのすべての参照が示されます。

または、インスタンス名の横にある矢印をクリックして、そのすべてのフィールドを表示してから、フィールド名をクリックして、そのすべての参照を表示します。 フィールドのインスタンスの詳細を表示する場合は、フィールドを右クリックして、[Go to Instance] を選択します。

  1. [References] タブで、メモリリークが発生している可能性のある参照を特定するには、タブを右クリックして、[Go to Instance] を選択します。 これにより、ヒープダンプから対応するインスタンスが選択され、そのインスタンス データが表示されます。

ヒープダンプでは、割り当てられた各オブジェクトのスタック トレースはデフォルトで表示されません。 スタック トレースを取得するには、[Dump Java heap] をクリックする前に、メモリ割り当ての記録を開始する必要があります。 その後、図 5 のように、[Instance View] でインスタンスを選択し、[References] タブの横に [Call Stack] タブを表示することができます。ただし、割り当ての記録を開始する前に、いくつかのオブジェクトが割り当てられている可能性があり、これらのオブジェクトのコールスタックは利用できません。 コールスタックを含まないインスタンスは、アイコン上の「スタック」バッジ で示されます (残念ながら、スタック トレースでは、ユーザーが割り当ての記録を実行する必要があるため、Android 8.0 を使用している場合は、現在、ヒープダンプのスタック トレースを表示できません)。

ヒープダンプでは、次のいずれかが原因で生じたメモリリークに注目します。

図 5. ヒープダンプの取得に必要な時間がタイムラインに示されます。

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

クラスリストの上部で左のプルダウン リストを使用して、次のヒープダンプに切り替えることができます。

デフォルトでは、ヒープのオブジェクト リストはクラス名別に並べられます。 もう 1 つのプルダウン リストを使用して、次の並べ方に切り替えることができます。

デフォルトでは、リストは [Retained Size] 列で並べ替えられます。 いずれかの列の見出しをクリックして、リストを並べ替える方法を変更できます。

[Instance View] ペインでは、各インスタンスの次の情報が表示されます。

ヒープダンプを HPROF で保存する

ヒープダンプを取得した後は、Memory Profiler の実行中にのみデータが表示されます。 プロファイリング セッションを終了すると、ヒープダンプは失われます。 したがって、後でレビューするためにヒープダンプを保存する場合は、タイムラインの下にあるツールバーで [Export heap dump as HPROF file] をクリックして、ヒープダンプを HPROF ファイルにエクスポートします。 表示されたダイアログで、ファイルに .hprof 拡張子を必ず付けて保存します。

その後、そのファイルを空のエディタ ウィンドウにドラッグすると(または、ファイルタブ バーにドロップすると)、Android Studio でファイルを再度開くことができます。

jhat など、別の HPROF アナライザーを使用するには、HPROF ファイルを Android 形式から Java SE HPROF 形式に変換する必要があります。 android_sdk/platform-tools/ ディレクトリにある hprof-conv ツールを使用して、この変換を行えます。 元の HPROF ファイルと変換済みの HPROF ファイルの書き出し先の 2 つの引数を指定して、hprof-conv コマンドを 実行します。 次に例を示します。

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

メモリをプロファイリングするためのテクニック

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

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

ヒント: 上述の手順は、monkeyrunner テスト フレームワークを使用しても実行できます。

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

WeChat で Google Developers をフォローする

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (Dec 2017 Android Platform & Tools Survey)