Androidでメモリリークの調査と、そのヒープダンプから画像を抽出する

Androidアプリを開発していると、画像の取り扱いでメモリリークしていたり、OutOfMemoryErrorが発生する事があります。
そのような場合は、Javaヒープメモリのダンプを取り、どのBitmapがメモリリークしているか、または無駄にメモリを食っていないかを確認し対応します。また、Javaヒープダンプから画像データを抽出し、具体的にそのオブジェクトがどの画像かを確認します。

この記事でしていること。
・Androidアプリのヒープダンプを取得してメモリリークの調査。
・ヒープダンプからBitmapを復元し画像を確認。

用意するもの。
・DDMSでヒープダンプを取得するためAndroid SDK
・ヒープダンプを解析するためのMemory Analyzer(MAT)
・ヒープダンプから抽出した画像を確認するためのGIMP

まずヒープダンプを取りましょう。
解析対象のプログラムを動作させた状態で、USBをつなぎます。
解析対象のプログラムはdebuggableをtrueにしておく必要があります。
そしてDDMSからそのプログラムを選択し、ヒープダンプ取得ボタンを選択します。
memleak1
するとヒープダンプとしてhprofファイルを保存することができます。

AndroidのヒープダンプはAndroid用のフォーマットのため、そのままではMATでhprofファイルを開くことができません。
そこでフォーマットを変換してやる必要があります。
adt-bundle-windows-x86\sdk\toolsにある、hprof-conv.exeを使い変換します。
引数は次のような感じで、入力ファイルと変換後の出力ファイルを指定します。

HOGE> hprof-conv.exe com.example.memoryleaktest.hprof memleakcheck.hprof

変換が終わったらMATのメニューから、[File]-[Open Heap Dump…]で変換したhprofファイルを開きます。
次のダイアログが表示されますが、そのままFinishでOK。
memleak2

そしてDoninator Treeを開きます。
するとメモリを90%使ってる怪しいやつが・・・。
memleak3

ツリーを開くとBitmapのインスタンスと判明。
なぜメモリが解放されないかというと、そのインスタンスへの参照を誰かが保持してためなので、それを確認します。
byte配列を右クリックし、[Path To GC Roots]-[exclude weak/soft references]を選択します。
memleak5
するとbitmapListから参照されているので解放されないと言うのがわかります。

次に、このbyte配列が具体的にどんな画像かを確認します。
byte配列を右クリックして[Copy]-[Save Value To File]を選択。
memleak4

保存したファイルをGIMPで開きます。
開く際はファイル形式の選択でRaw画像データを選択します。

画像の種類はRGBアルファを選択します。
なんだか画像が崩れてますね。
memleak6

幅を変更していくと・・・。
memleak7

無事に画像が復元できました。実際のバグで画像を確認できると「犯人はおまえかぁっ!」となり、後はハッピーエンドですね。
memleak8

ちなみに今回メモリリークさせたのは次のコードでした。再現コードにしては適当すぎてアレですが・・・。
Bitmapを生成して、それをリストに入れて放置。当然GCされませんね。


for(int i = 0 ; i < 100 ; i++){ Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.android); bitmapList.add(bitmap); }[/java]

No Comments

Post a Comment

コメントを投稿するには、下の計算の答えを入力する必要があります。答えは半角数字で入力してください。 * Time limit is exhausted. Please reload the CAPTCHA.