Flash AS3のガベージコレクションを攻略する4つのTips
Flash AS3でCPU負荷の高い処理、多くのメモリを使う処理、アニメーションを長時間連続して再生する処理を行う場合には、メモリの管理をすることが必須です。
Flash AS3においてメモリ管理について知っておくべきTipsをまとめました。
その1 ガベージコレクションについての基本知識を仕入れましょう
リファレンス(参照)、メモリスコープ、メモリ空間におけるアドレスとデータについて、ガベージコレクションがなにをしているのか知る必要があります。
hoge = null;
delete hoge;
の二つの違いも重要です。C, C++とは、これらの意味が異なりますので、押さえておきましょう。
ガベージコレクションにおいて一番難しい概念は「参照回数」でしょう。C++などのクラス設計においても参照回数という概念はありましたが、計測方法が異なります。
参考URLは
その2 Tweenを連続して使用するとときどき「かくっ」とアニメーションが止まるのを防ぐ
Tweenは決してノンストップで使えないわけではありません。ノンストップでTweenを使ったときにアニメーションがカクカクするのはGCが効き過ぎるために生じる現象です。具体的に説明します。
function loop(){
var foo:Tween = new Tween(test,100,10,,,);
}
というコードにおいて、loop関数をTimerで回して、testオブジェクトをノンストップで動かしています。これを実際に動かすと、ときどきTweenの動きがカクカクとして止まることがあるはずです。
それは、Tweenをローカル変数で参照しているために、関数を抜けると参照回数が0にセットされ、次回のGCのタイミングで、オブジェクトが削除され、その瞬間にアニメーションが止まるからです。単発でTweenを使うとアニメーションが止まらないのは、単に「Tweenの動きが終わるまでにGCが発動する確率が低い」からであって、止まる可能性は現実にあります。
Tweenと同様に、Loader, Sound, Timerなどのオブジェクトで同様の問題が起きる可能性があります。
回避のポイントは変数の参照を消さないことです。つまり上記のコードは以下のようにすることで修正できます。
var foo:Tween;
function loop(){
foo = new Tween(test,100,10,,,);
}
しかし、このようにグローバル変数を使うとコードが読みにくくなります。
また、今度は逆に、参照が消えないために、GCの対象にならず、メモリリークを発生させる原因になります。
気をつけましょう。
この問題に対処する現実的な方法は、
- 上記の動作を認識した上で、動作完了後に参照回数をゼロにするようなコードを自分で書く
- GCに対応するようにカスタマイズされたTweenクラスを使う
の二つです。
その3 メモリがどんどん肥大化していってFlashが死ぬ問題に対処する
これは使わなくなったオブジェクトの参照がどこかで残っているために、GCが発動してもデータが削除されないことによって発生します。
一つ一つのオブジェクトの参照を調べていき、バグをつぶしていくしかありません。
しかし、これらを防ぐためのテクニックはあります。
- 変数のスコープを限定するようにする。(ただしこれを実践しすぎると、上述の「その2」の罠にはまる可能性があります。バランスが大事です)
- イベントハンドラに気をつける。addEventListnerを使うと、そのときに対象オブジェクトに対して、参照関係が作られ、参照カウントが増加します。この参照カウントは適切にremoveEventLisnerしてやるまで減りません。しかし、作成したEventを一つ一つ追っていくのは非常に面倒ですので、現実的には、addEventListnerしても参照関係を作らない「弱参照」というテクニックを使うのが有効です。
その4 任意のタイミングでGCを発動するテクニック
Flashのデフォルトの動作では、GCはFlashによって自動的に発動し、開発者が任意のタイミングでGCを発動することはできません。また、描画負荷の高いときはFlashのアニメーションをなるべく止めないように、GCの発動が自動的に止まります。すなわち、コードの設計が適切でない場合は、
負荷があがる->GCが発動しない->ますます負荷があがる->死ぬ
という悪循環をたどることになります。
では、負荷が高くないすきを狙って、手動でGCを強制発動する方法があるのかどうか。
以下のサイトに、LocalConnectionを用いて、任意のタイミングでGCを発動させるバッドノウハウが載っていますが、あやしいです。
system.gc();
この方法は、公式ですが、デバッグモードのときとAIRアプリのみ動作します。
結論としては、開発者が任意のタイミングでGCを発動することは難しいといえます。その1,その2、その3のTipsを活用して、適切なコードを書いていくしかありません。
私見
個人的な感想を述べるなら、Flash Action Scriptはメモリ管理なぞ考えなくてよいところが利点だったのに、その時代は終わったと感じています。そして、ガベージコレクションの登場によって、メモリ管理は一見、C++の時代にくらべて易しくなったように見えて実はブラックボックス化が進み、泥沼にはまると問題を発見しにくい構造になったと感じています。
「ポインタを制するものがCを制す」はそのまま
「ガベージコレクションを制するものがAS3を制す」に置き換えることができ、メモリ管理はいまも昔もプログラミング学習の最大の難関です。