Android でアプリケーション メモリを分析する方法 (17) - MAT を使用して Android ヒープを表示する
前回の記事では、Android プロファイラーのメモリ プロファイラーを使用して Android ヒープの状況を表示する方法を紹介しました。
たとえば、Android ヒープにはどのようなオブジェクトがあり、これらのオブジェクトの参照は何かなどです。
ただし、依然として比較的厳しい課題に直面しています。アプリ開発者であってもメモリ アナライザーであっても、ヒープには Android ネイティブ クラスだけでなく、サードパーティ ライブラリで使用されるクラスも含む多くのオブジェクトが存在します。これらのクラスは、使用中に、浅いサイズまたは保持されたサイズが大きいため、メモリ分析を混乱させる可能性もあります。
このような問題を解決するには、異なる時点でヒープを介して差分比較を実行することを好みます。つまり、時刻t1で生成されたヒープheap1と時刻t2で生成されたヒープheap2とが比較される。
この目的を達成するために、Java 開発における重要なメモリ分析ツールである MAT を紹介します。
MATを使用する前の準備
前の記事では、AS を使用してヒープをキャプチャしましたが、今度はそれをエクスポートして MAT ツールで使用する必要があります。以下に示すように:
次に、Android によって保存されたヒープ ダンプの形式が MAT のニーズを満たすように変換されます。形式を変換するためのツールは Android SDK に含まれています。次のように:
/Users/biaowan/Library/Android/sdk/platform-tools/hprof-conv ./mat/memory-test_malloc_int\[\].hprof mat_test1.hprof
マットの使用
MAT を開いた後、メニュー バー -> ファイル -> ファイルを開く に移動します。次に、変換したばかりの mat_test.hprof ファイルを選択します。以下に示すように
メイン インターフェイスには概要インターフェイスが表示されていることがわかります。このインターフェイスでは具体的な詳細が英語で説明されているため、繰り返しません。上の画像の 8 つのマーカーについて以下で説明します。
-
マーク 1: 概要インターフェイスを開きます。それが上記のメインインターフェイスです。
-
マーク 2: 現在のヒープ内のオブジェクトのディストリビューションを開きます。デフォルトではクラス名でソートされています。右クリックで対応する操作を行うことができ、それぞれの操作の意味をマーキングしてあります。以下に示すように
-
マーク 3: このヒープ内のすべてのドミネーター ツリーを表示します (注: ドミネーター ツリーは、前の記事で紹介されています: Android がアプリケーション メモリを分析する方法 (16) - AS を使用して Android ヒープを表示します: http://t . csdn.cn /GTWpR ).各オブジェクトは対応するドミネーター ツリーをどのように表示する必要があります。マーク 2 に対応する右クリックの手順を参照してください。
-
マーク 4: SQL ステートメントを使用してクエリを実行するのと同様の Open Object Qurey 言語。MAT が提供するメニュー機能は Android で使用するのに十分であるため、この記事では再度紹介しません。
-
マーカー5: スレッド名、スタック、ローカル変数などを表示します。ただしAndroidにはこの機能がないので使用できません
-
マーク 6: 以下にマークされているように、さまざまなレポートを印刷します。
-
マーク7:マーク2の右クリックでサポートされる各種操作です。詳細はマーク2を参照してください。
-
マーク8:検索ボタン、住所から検索できます
MAT の操作方法を詳しく説明するために、さまざまな質問をテンプレートとして、操作プロセスを詳しく紹介します。
質問 1: 特定のクラスにどのようなオブジェクトがあるかを確認する方法
- マーカー 1 をクリックして、すべてのクラスのリストを開きます。
- 最初の行に、検索するクラスを入力するか、さまざまなサイズで並べ替えてフィルターします。
- クラスを選択し、右クリックして「オブジェクトのリスト」を選択します。次に、必要に応じて各オブジェクトをリストします。
質問 2: オブジェクトから GC ルートまでの参照チェーンを確認する方法
- オブジェクトを選択し、右クリックして「GC ルートへのパス」を選択します。
- すべての xxx 参照を除外するを再度選択します
参照チェーン全体が明確で明確であり、描画命令には含まれていないことがわかります。
質問 3: オブジェクトのドミネーター ツリーを表示する方法
- オブジェクトを選択し、右クリックして「Java Basics」を選択します。
- もう一度「Dominator ツリーで開く」を選択します
- ポップアップ ボックスで、[完了] を選択します。デフォルトでオブジェクトごとに並べ替え
この図から、TaskRunable オブジェクトが 2 つのオブジェクト (int 配列と弱参照) を直接制御していることがわかります。
質問 4: オブジェクトの直定規を確認する方法
- オブジェクトを選択し、右クリックしてイミディエート ドミネーターを選択します
- ポップアップボックスで「完了」を選択します
選択した TaskRunable オブジェクトの直接コントローラーが Task オブジェクトであることがわかります。
質問 5: クラスローダーと同じクラスが繰り返しロードされているかどうかを確認する方法
- マーク 1 をクリックして概要インターフェイスを開きます
- インターフェイスを一番下までスクロールします
- 重複クラスを選択します
質問 6: ヒープの中で最も多くのメモリを占有している部分を確認する方法
- マーク 1 をクリックして概要インターフェイスを開きます
- 一番下までスクロールします
- 上位顧客を選択してください
図からわかるように、最もメモリを消費する部分がオブジェクト、クラス、クラス ローダー、およびパッケージ名に従ってリストされています。
質問 7: ヒープ レポートの表示方法
- マーク6をクリックします。「ヒープダンプの概要」を選択します。
- レポートで、目次をクリックして目次を表示します (このフィールドはレポートの下部にあります)。
最も多くのメモリを占有しているオブジェクトを直接表示することもできます。
質問 8: 漏れチェックの実施方法
- マーク 6 をクリックし、漏洩の疑いを選択します
写真からわかるように、容疑者は 3 人います。下にスクロールすると、次のように 3 人の容疑者の詳細が表示されます。
図では、コンテンツの 29.51% を占める 2100 個のインスタンスを持つ Task クラスが簡単に説明されています。詳細をクリックすると、対応する参照チェーン パスが表示されます。GC ルートの参照チェーン全体がはっきりと確認できます。
複数のヒープに対して差分分析を実行してメモリの問題を見つける
差分分析に複数のヒープを使用する方法を段階的に練習するために、前の記事の解決策 2 の例を選択します: Android アプリケーションのメモリを分析する方法 (16) - AS を使用して Android ヒープを表示します: http: //t . csdn.cn/JYGFC . 次に、同じプロセス内の 2 つの異なる時点で、as_heap1.hprof および as_heap2.hprof と呼ばれる異なるヒープが選択されます。
シナリオ 1: MAT が 2 つのヒープ間のメモリ リークを自動的に分析する
-
上記の hprof-conv ツールに従って、as_heap1.hprof と as_heap2.hprof をそれぞれ mat_heap1.hprof と mat_heap2.hprof に変換します。次にマットツールで開きます。
-
1 とマークされている 2 番目のヒープの概要操作バーを開きます。一番下までスクロールします
-
スナップショットの比較による漏洩の疑い。
-
ポップアップ ボックスで mat_heap1.hprof を選択します。次に、「完了」をクリックします。mat_heap2.hprof と mat_heap1.hprof に差分解析を実行させ、次のようにレポートを生成します (しばらく待つ必要があります)。
図からわかるように、リークの疑いのある com.example.test_malloc.Task オブジェクトには 4900 個のオブジェクトがあり、ヒープ全体の 49.26% を占めています。以下に示すように、詳細をクリックして参照チェーンを表示します。
図からわかるように、タスクは DeviceManager のリスナーによって保持されているため、GC はタスクをリサイクルできません。これでメモリリークポイントが判明しました。
シナリオ 2: 自動分析が実行できない場合の手動分析
2 つのヒープ間の間隔が短く、リークしたオブジェクトがヒープ全体で占めるスペースが少ない場合、現時点では mat は自動分析を実行できません。この時点で、手動で分析できます。
次に、それぞれ mat_heap3.hprof と mat_heap4.hprof という、より短い時間間隔で 2 つのヒープを使用します。
注: mat_heap3.hprof と mat_heap4.hprof は、AS によって近い時間間隔で再フェッチされる 2 つのヒープです。
-
mat を使用して mat_heap3.hprof および mat_heap4.hprof を開きます
-
質問6に従って、最もメモリを消費する部分を出力してください。以下は mat_heap3.hprof のレポートです。
このことから、最大のメモリを占有する主なオブジェクトは int 配列であることがわかります。次に、2 つのヒープ内の int 配列間のギャップ、つまり、どの int 配列が mat_heap3.hprof よりも mat_heap4.hprof に多く存在するかを手動で分析します。
-
2 とマークされている mat_heap3.hprof の統計をクリックします。次に、int[] を選択します。右クリックしてすべてのオブジェクトをリストします。以下に示すように
-
操作履歴バーをクリックし、list_objects... を右クリックして、[追加] をクリックしてバスケットを比較します。以下に示すように
-
2 つのヒープの int[] ステータスを比較する必要があるため、mat_heap4.hprof を選択した後、手順 3 と 4 に従って同じ操作を実行します。比較バスケット ウィンドウでは、比較する必要がある 2 つのオブジェクトがあります。次に、感嘆符をクリックして比較を開始します。次のように:
テスト結果は単純ソートされ、shallow_heap #1が昇順にソートされます。heap3 には存在しないが heap4 には存在するオブジェクトを表示できます。これは、heap3 を取得した瞬間と heap4 を取得した瞬間の間のヒープ内の追加の int 配列オブジェクトでもあります。最前列の 10 個のオブジェクトの場合。
前の質問 2 によると、その参照チェーンを確認して、誰がそれを保持しているのか、なぜ解放されないのかを分析できます。
ステップ 2 では、出力トップ comsumer には int 配列以外のオブジェクトがあるため、ステップ 3、4、および 5 に従って 2 つのヒープを比較します。int[] を例にして詳しく説明しましたので、1 つずつ比較することはしません。
比較対象のオブジェクトを特定するのに役立つトップ消費者を使用することに加えて、疑わしいオブジェクトも比較できます。手順はまったく同じです。
この時点で、MAT の使用の入門は完了です。
MAT は、メモリ解析における AS の次の欠点を補います。
- 保持セットをカスタマイズできません (これは大規模なアプリケーションに役立ちます)
- アドレス検索を実行できません
- ヒープ間を比較できません
- 希望どおりに並べ替えることができません
- 必要に応じてフィルタリングできないなど。
MAT は十分強力ですが、未解決のメモリ問題がまだあります。どのスレッドがメモリ リークを引き起こしているのか、また、どのような種類のコール スタックがあるのかを知るにはどうすればよいでしょうか。
マルチスレッド プログラミングでは、オブジェクト間の不当な参照や、不当なプロダクション スレッドとコンシューマ スレッドなどのスレッド間の不当なロジックが原因でオブジェクトのリークが発生する可能性があります。MAT は Android スレッドによって引き起こされるメモリ リークを解決できません。
次は、このようなマルチスレッドによるメモリリークをツールを使って発見する方法をご紹介しますので、ご期待ください。