第 1 章 駄目コードとその対策 6
1.11 OutOfMemoryError 避けが悪影響だけ出している
ServiceCon-nection
のリークに繋がります。この例ではService
をbind/unbind
するタイミングはon-Start()/onStop()
を使用していますが、onCreate()/onDestroy()
やonPause()/onResume()
など、
Activity
のライフサイクル上で必ず通る組み合わせを使用するとリークを回避することができます。
Eclipse
の場合、このプラグインをインストールすると、DDMS
パースペクティブのDump
HPROF file
ボタンを押すとダンプしたメモリを解析できるようになります。図1.2 Dump HPROF fileボタン
たとえば次のような無駄に大きな
byte
配列をもつActivity
の解析をしてみましょう。メン バ変数として16MB
のbyte
配列を保持しています。リスト1.34: LargeBytesActivity.java
public class LargeBytesActivity extends ActionBarActivity { private byte[] mLargeBytes = new byte[16 * 1024 * 1024];
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} }
この
16MB
のbyte
配列をメモリリークの原因ということにして、MAT
を使って追ってみ ましょう。まず最初に
Dump HPROF file
ボタンを押します。すると図1.3
のような画面が表示され ます。この画面ではおおよそメモリを大きく使用しているクラスが何であるのかが円グラフ で表示されます。図1.3 Memory Analyzerの初期表示
今回はメモリを大きく保持しているクラスが何であるかを確認したいので、ヒストグラムを 表示します。ヒストグラムを表示するには図
1.4
のヒストグラムのボタンを押します。図1.4 Create a histogramボタン
すると図
1.5
のように、各クラスのインスタンスの数と、どれだけメモリを占有している かが表示されます。今回の場合ではbyte
配列が一番大きくメモリを占有しているのがわかり ます。図1.5 Histogram
次に
byte
配列のインスタンスを個別に見てみましょう。図1.6
のように右クリックのメ ニューのList objects
からインスタンスの一覧を表示できます。図1.6 List objects
リストの
Retained Heap
で並べ替えると図1.7
のように一番大きい、16MB
のbyte
配列 を見つけることができました。図1.7 16MBのbyte配列
次にこの
byte
配列はどこから参照されているのか、そしてなぜGC
によって回収されない のかを調べます。調べるには右クリックのメニューからPath to GC Roots
を選択すること で解析できます。図1.8 Path to GC Roots
解析結果は図
1.9
のようになります。するとこの16MB
のbyte
配列は、mLargeBytes
と いう変数名でLargeBytesActivity
というクラスが保持していることまで特定できました。図1.9 リーク原因の特定
OutOfMemoryError
が発生したとき、仮説を立てて修正し、再発するかを確認する方法がありますが手間がかかります。
MAT
を使えばどのクラスがどれだけのメモリを専有し、それ が何故GC
によって回収されないのかを特定することができます。実際にメモリリークが発 生している場合も同じような手順で特定することができます。駄目なコードでは原因を特定せず、そこら中で使い終わった変数に
null
を代入していたり しますが、結局根本的な解決になっておらず、null
落ちや可読性の低下に陥っているのを見か けます。仮説を立て修正することも大切ですが、MAT
のように使えるツールがあるので、ま ず解析を行い原因を特定してから修正をするようにしましょう。
ドキュメント内
Android実践プログラミング 現場で生まれた設計パターン
(ページ 43-51)