Andoid アプリ作ってる/はじめたけど、まだ MAT を使ったことがない方
- MAT を使ってみようした事はあるものの、画面から難しそうな雰囲気を察知し、起動10秒後にはそっとタブを閉じてしまった経験がある方
- DDMS の基本的な使い方を理解している方
Memory Analyzer ってなに?
Project の元で開発されている、メモリ使用量を分析する為のソフトウェアです。
VM 上でどのオブジェクトが沢山生成されているか、メモリ使用量が大きいか、などを分析してくれるツールです。 専用のツールという訳ではなく、 開発全般で使う事ができます。
配布形態としては Plugin 形式と、単独で起動する Standalone 形式があるので、まだインストールされていない方は、下記の記事等を参考にお好みの方をインストールしてください。
※この記事では Plugin 形式を想定して説明します。
- 公式 …
- Blog で使うMemory Analyzerのインストール …
おさえる基本事項 3 つ
MAT はとても強力なツールではあるものの、機能が多く若干複雑で取っつきにくい印象があります。
その為、この記事では MAT のすべての機能を網羅するのではなく、ポイントを基本的な3つだけに絞り紹介します。
- Leak Suspects - リークしてそうなのはこいつらだ
- Dominator tree - 大きいオブジェクトを一覧で見よう
- Path to GC Roots - こいつは誰が参照してる?
リークを特定する為の基本的な流れとしては、Leak Suspects で怪しいところを教えてもらい、Dominator Tree で怪しいオブジェクトを一覧 & 構造を調べ、Path to GC Roots でリーク元を特定する、という様な流れになります。
では、実際に
を持つプロジェクトの実例を交えながら見ていってみます。のサンプル Activity
簡単な例ですが、下記の様な
このプロジェクトは github でもホストしていますので、良ければ clone して手元で試してみてください。
を起こしてみる
上記のプロジェクトを起動し、DDMS を開き、対象のプロセスを選択すると、起動直後のメモリ使用状況は 5M 程度の使用状況だと思います。
しかし、このアプリケーションの画面を回転(
の場合は Ctrl + F11)し、また画面を元に戻しても、使用メモリが増えたままで起動直後の水準に戻らなくなります。やったね、 の発生です。
MAT を起動する
実際のケースでは
が起きていると確信を得ている状況ではないかもしれませんが、上記の様に の疑いがある状態で、DDMS の 「Dump HPROF file」をクリックし、その時点でのメモリ使用状況をダンプします。尚、 r14 辺りから、デフォルト設定ではダンブすると MAT で開くのではなく、一度ファイルに保存する様になっている為、DDMS の設定の「HPROF Action」を「Open in 」に変更しておくと便利です。
MAT 起動後の画面
初回の起動であれば、下記の様な画面が表示されるので「Leak Suspects Report」を選択します。
もし、ダイアログが表示されない場合も Overview という画面のフッタにリンクがあり、そこから開けます。
Leak Suspects - リークしてそうなのはこいつらだ
この画面では、要はメモリ使用量が大きいか、または存在するオブジェクト数が多いなどの理由で、MAT がと判断した問題の分布がパイチャートと一覧で表示されます。 の疑いがある
その為、基本的な使い方の流れとして、
しているオブジェクトが何かの確証が得られてない場合など、まずこの Leak Suspects を参照し、どのオブジェクトが を起こしていそうかを、MAT に教えてもらう事からはじめるのが良いと思います。もし、既に
していそうなクラスなどがはっきりしている場合は後述の Dominator Tree から を辿るのが早いでしょう。注意しなければいけない点としては、あくまでものを表示しているに過ぎない為、最終的には していそうなユーザー自身が意図しないオブジェクトが存在していまっていないかを判断する必要があります。
これは MAT を使う上での重要な事で、 はシステムによって疑わしき箇所を検出する事は出来ますが、確実に を起こしていると判断する事はできません。最終的には MAT のユーザー自身が判断する必要があります。
大事なことなので2回言いました。
話を戻して Leak Suspects の画面に戻ると、どうやら me.tlync. が2つ存在し、それぞれ 3MB 近く消費している事が分かります。
.example.MemoryAnalyzerDemoActivity の byte[]今回のケースでは、Activity に someBigObject という static 変数を持っており、その変数に3MB近い byte 配列が代入されている為、3MB を消費している状態自体は
でも何でもありませんが、それが2つ存在するのは明らかにおかしいと判断する事ができます。では、何故この様な状態になっているか、を詳細に追求する為に Dominator Tree という、メモリ使用量の大きなオブジェクトの一覧を参照する機能を利用します。
Dominator Tree - 大きいオブジェクトを一覧で見よう
MAT のレポート上部にあるツリー型のアイコンをクリックすると、メモリ使用量(Retained Heap)の多い順にオブジェクトの一覧が表示されます。
今回のケースでは分かりやすくトップ2つがまさに問題としてレポートされていたオブジェクトですが、実際のケースでは一覧上には表示されないケースもあります。
その時は、一覧の一番上の
※この記事では触れませんが、Dominator Tree に似た Histgram という View を利用すると、xxActivity 毎という様な型毎のオブジェクト数、メモリ使用量を一覧する事ができます。
一覧上で
していそうなオブジェクトを見つけたら、ツリーを展開し、最終的にメモリ使用量が大きなプロパティを特定します。上記の例では、下の方の Activity のツリー構成は Activity の
が byte オブジェクトを持っているだけで特に問題は無い様に見えますが、上の方の Activity のツリー構成からはクラス > インナークラスの参照があるなど、何となく怪しい雰囲気が漂っています。今回のケースは凄く分かりやすい誰が参照を保持していてGC されないのかを特定します。
である為、現時点で答えは出ている様なものですが、念の為、Path to GC Root というメニューから、なぜ GC されないのか、つまりPath to GC Roots - こいつは誰が参照してる?
対象のプロパティを選択し、右クリックし Path to GC Roots > with all referencesを選択します。
with all references は、文字通りすべての参照を見る事が出来ますが、SoftReference や WeakReference などを除いて見たい時は、他の exclude ** references を選択してください。
SoftReference と WeakReference ってなんぞ? という方の為に補足すると、SoftReference は OOM になりそうになると解放される参照で、WeakReference は GC の度に優先的に解放される参照です。これらが
となる事は実質無い為、常に exclude weak/soft references を選択しても構いません。Path to GC Roots を実行すると、下記の様な画面が表示され、選択したオブジェクト(今回の場合は byte[])が、どの様なツリーで参照されていて GC されないかが分かります。
上記のケースでは、Effective で有名な、非 static なインナークラスは、アウタークラスへの暗黙的参照を持つ、という特性からくる が発生しています。
その為、今回の対策としては非 static なインナークラスを static に変更し、Context が必要な doSomething には Application Context を渡してやるか、もしくはそもそも innerClass を static に保持する必要が無ければ、変数の方を非 static にする事で
を解決する事が出来ます。まとめ
一見すると難しそうな MAT ですが、基本的な部分を扱うだけならさほど難しくないよ、という事をひとりでも多くの に認識してもらえれば何よりです。
が、MAT はまだまだ機能が豊富で、例えば OQL という n byte 以上のオブジェクトという様な に似た構文でオブジェクトを探せたり、2点間のヒープダンプの差分を出力できたり、と紹介しだすとキリがないほどで、自分自身も知らない機能は沢山ある為、少しずつ使い方を学び、この世の アプリから を撲滅しましょう。
0 件のコメント:
コメントを投稿