かなり今更ですが、
「そのオブジェクト、本当に消えていますか?」で、ガベージコレクトは参照カウント方式で行われると書きましたが、
どうやらマーク・アンド・スウィープ方式と呼ばれる方法で行われているようです。
参考:akihiro kamijo: Flash Player 9 のガーベジコレクション
参照カウント方式
参照カウント方式の場合、循環参照しているオブジェクトは永久に破棄されません。
矢印は参照方向、数字は参照カウントを表しています。
最下段の二つのインスタンスは、お互いに参照し合っています。(循環参照)
ここで、図のように参照を切ったとしても、、
依然として、最下段のインスタンスの参照カウントは0にはならず、
いつまでたってもガベージコレクトの対象にはなりません。
マーク・アンド・スウィープ方式
マーク・アンド・スウィープ方式では、循環参照の問題は起きません。
いままで僕はオブジェクト同士が循環参照にならないように気を使っていましたが、
徒労だったというわけです。
マークアンドスウィープ方式は以下のような感じです。
ルート(おそらく、StageやGlobal)から参照をたどれるオブジェクトにマークをつけていきます。
参照をたどれるオブジェクトがなくなった時点で、まだマークされていないオブジェクトがあったら、
ガベージコレクトの対象となります。
たとえば、以下のように参照を切った場合、
最下段の二つのインスタンスはルートから参照をたどれなくなるので、
ガベージコレクトの対象となり破棄されます。
FlashPlayerが参照カウント方式でガベージコレクトして「いない」ことを確かめるサンプルです。
循環参照しているインスタンスを意図的に生成しています。
FlashPlayerが参照カウント方式でガベージコレクトしていれば、
メモリ使用量が増え続けるはずですが、
実行するとある程度増えたところでお掃除される様子が確認できます。
(Flashのガベージコレクトはそんなに頻繁には行われないようです。
僕の環境だと600Mbほどメモリを使用したところで、ガベージコレクトされました。
ガベージコレクトされる頻度は、おそらく実行環境に依存すると思います。)
package
{
import flash.system.System;
import flash.events.Event;
import flash.display.BitmapData;
import flash.text.TextField;
import flash.display.Sprite;
import flash.utils.setInterval;
public class Main extends Sprite
{
private var _memoryViewer : TextField;
public function Main ()
{
//相互参照なオブジェクトの生成
setInterval(createEachReferencedObj, 1000);
//メモリ表示用テキストエリア
_memoryViewer = new TextField();
addChild(_memoryViewer);
addEventListener(Event.ENTER_FRAME, checkMemory);
}
/**
* 相互参照なオブジェクトを生成
*/
private function createEachReferencedObj () : void
{
var objA : Object = {bmp:new BitmapData(2000, 2000, true)};
var objB : Object = {ref:objA};
objA.ref = objB;
}
/**
* メモリ使用量を表示
*/
private function checkMemory (event : Event) : void
{
var bytes : int = System.totalMemory;
_memoryViewer.text = String(bytes >> 20) + " Mb";
}
}
}