[ActionScript 3.0] クロージャが原因のメモリリーク
クロージャを利用した場合、それを内包する関数(メソッド)におけるローカル変数の状態のまとまりを、そのクロージャが実際に呼び出されたときのために、スクリプトエンジン(Flash PlayerやAIR)が保持しつづける。
そのため、ローカル変数のオブジェクトは関数終了後も破棄されず、クロージャを保持しているオブジェクトが破棄されないかぎり、ずっとメモリを使用したままになる。
public class TestClosureMem extends Sprite { private var sampleTimer:Timer = new Timer(50, 20); private var checkTimer:Timer = new Timer(200); private var fnVec:Vector.<function> = new Vector.</function><function>; public function TestClosureMem() { sampleTimer.addEventListener(TimerEvent.TIMER, doSample); sampleTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete); checkTimer.addEventListener(TimerEvent.TIMER, checkMemory); sampleTimer.start(); checkTimer.start(); } private function checkMemory(e:TimerEvent):void { trace(System.totalMemory); } private function doSample(e:TimerEvent):void { var bmpData:BitmapData = new BitmapData(1000, 1000); fnVec.push( function ():void { // bmpDataをここで使わなくても、そのデータは保持されつづける } ); } private function onComplete(e:TimerEvent):void { // ここでfnVecを解放しないと大変なことになる。 // OSが不安定になることもあるので、テストのときは要注意。 fnVec = null; trace("--- closure cleared ---") fnVec = new Vector.</function><function>; sampleTimer.start(); } }
この例の場合、クロージャ(無名関数)をfnVecというVectorインスタンスが保持しているから、それが破棄されないかぎりメモリは増えつづけていく。
stageからの参照のつながり(スコープチェーン)は、以下のようになっている。
stage - TestClosureMem - fnVec - 各クロージャ
よって、TestClosureMemインスタンスかfnVecがガベージコレクション(GC)の対象にならないかぎり、bmpDataは破棄されずにどんどんメモリを占有していく。
GCの仕組みがある言語でも、メモリの問題からは逃れられない。そこにクロージャがからんでくるとさらに厄介なことになるので、特段の理由がないならばあまりうかつにクロージャを使わないほうがいいのかもしれない。
ディスカッション
ピンバック & トラックバック一覧
[…] # ActionScript 3.0についてはこちら。 ツイート カテゴリー: JavaScript タグ: 文法, 重要 この投稿のパーマリンク ← [Webアプリ] Flash製ノベルゲーム『TRUTH』 […]