メモリリーク経路を特定する
次に、Sphereクラスがなぜメモリリークを起こしているのかを調べます。不明なオブジェクトビューのSphereクラスの行をダブルクリックすると、オブジェクト参照ビューが表示されます。
左側のインスタンスペインに、Sphereクラスのインスタンスがリストされています。いずれかの行の行頭の三角マークをクリックすると、一段階展開されてFunctionと表示されました。これがこのSphereインスタンスを参照しているオブジェクトです。この参照が残っているために、このSphereインスタンスはガベージコレクションされません。
さらに、Functionを展開してみます。今度はflash.display.Stageと表示されました。その右側には、listenerN(Nは何らかの数値)と表示されています。これは、Functionを参照しているのはStageクラスのイベントリスナであるということを示します。つまり、StageクラスのイベントリスナがSphereインスタンスを参照しているためにメモリリークが発生していることが分かりました。
メモリリークの修正を行う
メモリリークの原因が分かったので、プログラムの修正を行います。原因はStageクラスのイベントリスナなので、stage.addEventListenerしている箇所を探すと、17行目と49行目に2箇所ありました。17行目の方は、ProfilerTuningクラス内にあるので特に関係なさそうです。49行目の方はSphereクラス内にあるので、こちらが原因と見て間違いないでしょう。このイベントリスナが適切にremoveEventListenerされていないためにメモリリークが発生しています。イベントリスナの残留は、メモリリークの典型的な原因の一つです。Sphereインスタンスは、自身がクリックされてremoveChildされる際にインスタンスも消えることが望ましいので、clickHandler内でイベントリスナを除去することにします。
private function clickHandler(event:MouseEvent):void {
event.stopImmediatePropagation();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, stageMouseMoveHandler);
parent.removeChild(this);
}
では、メモリリークが解消しているかどうかを検証します。再度プロファイラを実行して、メモリリークが発生していた手順を何度か繰り返した後にガベージコレクタを実行してみます。メモリの使用量グラフの青い線がガベージコレクタの実行タイミングで下がったと思います。また、ライブオブジェクトビューのSphereインスタンス数も0になりました。修正によって、メモリリークが解消したことが分かります。
おわりに
今回はプロファイラの基本的な使い方と、プロファイラを実際に使ってサンプルアプリケーションのメモリリークを解決するまでの流れを紹介しました。大規模なアプリケーションになるほど、実行中にCPU使用率が下がらない、メモリの使用量が増える一方で減っていかないなどの問題の解決には困難を伴います。小さいモジュール単位で実装中にこまめにプロファイラによる計測を行うことが、問題発生の防止に役立つと思います。

