Androidアプリケーションのメモリ分析

Dalvikにはガベージコレクタがありますが、これはメモリ管理を無視できるという意味ではありません。 それどころか、メモリを使用するときは特に注意する必要があります。メモリはモバイルデバイスで制限されることが知られています。 この記事では、アプリケーションがメモリを使用する方法を追跡するのに非常に役立つツールについて説明します。





メモリの過剰使用によるいくつかの問題は非常に明白です。 たとえば、アプリケーションでは、ユーザーが画面に触れると常にメモリリークが発生します。これにより、最終的にOutOfMemoryErrorが発生し、アプリケーションがクラッシュして閉じます。 これよりも微妙な他の問題は、アプリケーションおよびシステム全体のパフォーマンスの一般的な低下につながる可能性があります(ガベージコレクターはより頻繁に、より長い時間呼び出されるため)。



ツール



Android SDKは、アプリケーションによるメモリ使用量のプロファイリング用に、 DDMSの Allocation Trackerタブとヒープダンプの 2つの主要なデバイスを提供します。 Allocation Trackerは、アプリケーションに割り当てられたヒープの完全な状態に関する情報を提供しないため、特定の期間のメモリ使用量を知りたい場合に役立ちます。 Allocation Trackerの詳細については、「 メモリ割り当ての追跡」を参照してください。 この記事の残りの部分では、より強力なツールであるヒープダンプの研究に焦点を当てます。



ヒープダンプは、アプリケーションヒープ全体の状態のスナップショットであり、HPROF形式のバイナリファイルに保存されます。 Dalvikは、JavaのHPROFツールで使用される形式と類似した形式を使用しますが、まったく同じ形式ではありません。



実行中のAndroidアプリケーションのヒープをダンプする方法はいくつかあります。 1つ目は、DDMSの[ HPROFファイルダンプ ]ボタンを使用することです。 ダンプ作成のタイミングをより正確に選択する必要がある場合は、 android.os.Debug.dumpHprofData()メソッドを使用してプログラムで作成できます。



分析には、標準のjhatツールまたはEclipse Memory Analyzer(MAT)を使用できます。 ただし、最初に.hprofファイルをDalvik形式からJ2SE HPROF形式に変換する必要があります。 これを行うには、Android SDKに付属のhprof-convユーティリティを使用します。



hprof-conv dump.hprof converted-dump.hprof
      
      







例:メモリリークのデバッグ



Dalvikでは、プログラマは空きメモリに何も明示的に配置しないため、CおよびC ++のようにリークが発生する可能性があります。 「メモリリーク」は通常、不要になったオブジェクトを参照し続ける状況として理解されます。 場合によっては、単一のリンクによって、ガベージコレクターが呼び出して大量のオブジェクトを削除できないことがあります。



Android SDKのHoneycomb Galleryサンプルアプリを見てみましょう。 これは、Honeycombの新しいAPIの特定のメソッドの使用を示すシンプルなフォトギャラリーです。 ここで、メモリリークを意図的に作成して、デバッグ方法を示します。



画像




アプリケーションがネットワーク経由で画像を受信することを想像してください。 ユーザーの応答性を高めるには、最近表示した画像を保存するキャッシュを実装する価値があります。 これを行うには、 ContentFragment.javaにいくつかの変更を加えます。 クラスの先頭で、新しい変数を宣言して初期化します。



  private static HashMap<String,Bitmap> sBitmapCache = new HashMap<String,Bitmap>();
      
      







このマッピングでは、ダウンロードしたビットマップをキャッシュします。 これで、 updateContentAndRecycleBitmap()メソッドを変更して、ロードする前にキャッシュをチェックし、ロード後にビットマップをキャッシュに追加できます。



  void updateContentAndRecycleBitmap(int category, int position) { if (mCurrentActionMode != null) { mCurrentActionMode.finish(); } // Get the bitmap that needs to be drawn and update the ImageView. // Check if the Bitmap is already in the cache String bitmapId = "" + category + "." + position; mBitmap = sBitmapCache.get(bitmapId); if (mBitmap == null) { // It's not in the cache, so load the Bitmap and add it to the cache. // DANGER! We add items to this cache without ever removing any. mBitmap = Directory.getCategory(category).getEntry(position) .getBitmap(getResources()); sBitmapCache.put(bitmapId, mBitmap); } ((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap); }
      
      







ここで、メモリリークを意図的に作成しました。ビットマップはキャッシュに追加されますが、キャッシュからは削除されません。 実際のアプリケーションでは、キャッシュサイズを制限する必要があることは明らかです。



DDMSを使用したヒープ探索



Dalvik Debug Monitor Server(DDMS)は、Androidの主要なデバッグツールの1つです。 これは、Eclipse開発環境用のADTプラグインの一部であり、Android SDKのtools /フォルダーにもあります。 詳細については、 DDMSの使用を参照してください。



DDMSを使用して、アプリケーションによるヒープ使用量を分析しましょう。 DDMSは次の2つの方法で起動できます。





画像




左ペインでcom.example.android.hcgalleryプロセスを選択し、ツールバーの[ ヒープの更新を表示]ボタンをクリックします。 次に、DDMSの[ VMヒープ ]タブに切り替えます。 ヒープの使用に関する基本的な情報が表示されます。これは、ガベージコレクターが呼び出されるたびに更新されます。 最初の更新を表示するには、[ 原因GC ]ボタンをクリックします。



画像




「リビング」オブジェクトのメモリ占有量は8 MB弱です(「 割り当て済み」列を参照)。 写真をスクロールすると、この数値がどのように増加するかを確認できます。 アプリケーションには13枚の写真しかないため、メモリリークは制限されています。 ある意味では、これは起こりうる最悪のケースであるため、 ここでOutOfMemoryErrorが発生することはありません。



ヒープダンプ



DDMSツールバーの[ HPROFファイルダンプ ]ボタンをクリックし、 ファイルを保存する場所を選択して、 hprof-convユーティリティを使用して変換します。 この例では、MAT(1.0.1)の別バージョンを使用しますこちらからダウンロードできます



MATがインストールされたEclipseを使用している場合、「HPROFをダンプ」ボタンをクリックすると、ファイルは自動的に変換され、Ecilpseウィンドウで開きます。



MATを使用したヒープダンプ分析



MATを実行し、作成したばかりのHPROFファイルを読み込みます。 MATは強力なユーティリティであり、このトピック以外のすべての機能の説明です。そのため、ヒストグラムビューを使用して、リークを特定する方法の1つのみを説明します。 このビューでは、インスタンスの数、シャローヒープ(インスタンスが占有する合計メモリサイズ)、または保持ヒープ(参照するインスタンスとオブジェクトが占有する共有メモリ)で並べ替えられたクラスのリストを表示できます。



画像




浅いヒープでソートすると、バイト[]インスタンスの数が一番上にあることがわかります。 Android 3.0では、ビットマップはバイトの配列として表され、そのサイズはビットマップのサイズに依存します。 つまり、ビットマップに関係するメモリを表していることは明らかです。 バイト[]クラスを右クリックし、受信参照を含む[オブジェクトのリスト]を選択します。 ヒープに配置されたすべてのバイト配列のリストが表示されます。



ラージオブジェクトの1つを選択して開きます。 その結果、このオブジェクトのために形成されたリンクのチェーン全体が表示されます。 これがビットマップのキャッシュです!



画像




MATは、これらのオブジェクトが必要かどうかを知らないため、これがリークであるかどうかについて正確な情報を提供できません。 プログラマーだけが答えを知っています。 私たちの場合、キャッシュはアプリケーション全体に比べて非常に大きいため、サイズを制限することは論理的です。



MATを使用したヒープダンプの比較



メモリリークをデバッグする場合、異なる時点でのヒープの状態を比較すると便利です。 これを行うには、2つのHPROFファイルを作成します( hprof-convユーティリティで変換することを忘れないでください)。



これら2つのファイルを比較する方法は次のとおりです。

  1. 最初のファイルを開きます(「 ファイル」>「ヒープダンプを開く」 )。
  2. ヒストグラムビューを開きます。
  3. ナビゲーション履歴ビュー(ウィンドウ>ナビゲーション履歴)で、 ヒストグラムを右クリックし、[ 比較バスケットに追加 ]を選択します。
  4. 2番目のファイルを開き、手順2と3を繰り返します。
  5. [バスケットの比較]ビューに切り替えて、[ 結果の比較 ]をクリックします(右上隅にある赤い感嘆符アイコン)




おわりに



この記事では、Allocation Trackerとダンプヒープがメモリ使用量の分析にどのように役立つかを示しました。 また、MATがアプリケーションのメモリリークの追跡にどのように役立つかを示しました。 MATについて詳しく知りたい場合は、次の記事を読むことができます。




All Articles