Tweenerのメモリ解放の挙動について調べている中で、そもそもFlashPlayer9(AVM2)のガベージコレクタの挙動が気になったので、簡単な検証コードを書いてみました。結構興味深い挙動をしていたことに気づいたのでメモ。ちなみにPlayerはWindowsデバッグ用プレイヤーを利用しています。これはガベージコレクタを強制的に呼び出すSystem.gc()がデバッグ用プレイヤーしか利用できないためです。
検証
検証用コードはこんなかんじ。Spriteオブジェクト作ってaddChildして、removeしてnullつっこんで、それぞれの過程でメモリ使用量を計測してるだけ。
package {
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.system.System;
public class GCTest extends Sprite{
public function GCTest() : void {
log("[0]" + System.totalMemory);
var sprite : Sprite = new Sprite();
log("[1]" + System.totalMemory);
addChild(sprite);
log("[2]" + System.totalMemory);
removeChild(sprite);
log("[3]" + System.totalMemory);
System.gc(); // A
log("[4]" + System.totalMemory);
sprite = null;
log("[5]" + System.totalMemory);
System.gc(); // B
log("[6]" + System.totalMemory);
}
}
}
さて、単純に考えるとオブジェクト作ったものを完全に解放しきっているので[0]の値と[6]の使用量は同じ値になるはず。でも僕の環境での結果はこんな感じでした。
[0]2240512 [1]2330624 [2]2334720 [3]2338816 [4]2334720 [5]2347008 [6]2334720
オブジェクトの作成、addChildまでは分かりやすく使用量は上がっていますが、
- GC実行直後はメモリ使用量が減らずに増える
- nullを代入してオブジェクトをGC回収対象にしても完全にメモリ解放されていない([0 ]!=[5 or 6])
なんてことがわかります。とりあえずGC強制呼び出しは悪影響っぽいのでGC呼び出しをやめて、上の例のA, Bの箇所をコメントアウトするとこんな結果になりました。
[0]2240512 [1]2330624 [2]2334720 [3]2338816 [4]2338816 [5]2342912 [6]2342912
null入れたらメモリ使用量上がってます。なにこれw
見直し
そこでremoveChildの定義を見直し。するとこんな風に書かれています。
DisplayObjectContainer インスタンスの子リストから指定の child DisplayObject インスタンスを削除します。削除された子の parent プロパティは null に設定されます。その子に対する参照が存在しない場合、そのオブジェクトはガベージコレクションによって収集されます。DisplayObjectContainer の子より上位にある表示オブジェクトのインデックス位置は 1 つ下がります。 DisplayObjectContainer
なるほどー。removeChildすると、もうそれだけでGCの対象になるわけですね。これremoveChildだけじゃだめで、null参照させないとダメだと勘違いしてました。あと、Twitter経由でこんなこと教えていただきました。
@ryo_katsuma add 分と new 分というニュアンスがちとわからないのだけど、 GC はゴミが残るので正確な差分は求められないかもしれません
via Twitter
@ryo_katsuma オブジェクト単位じゃなくてブロック単位で開放するらしいので。と、 kamijo さんのブログに書いてありました。
via Twitter
kamijoさんのblogの該当記事がまだ見つけられていないのですが、どうもGCは逐一オブジェクト単位でメモリ解放を実行しているのではない、ということのようですね。また、null参照させたりして、解放の対象にさせても完全にクリーンアップしてくれることを保証してくれるわけでは無さそうです。
そんなわけで
GCの挙動を追っかけるためには、仕様をもう少し読みこなさないとダメぽいです。あとJavaのGCについてももう少し調べたいと思いました。
- Newer: MacOSXでTamarinをビルド
- Older: Twitterのロゴの色が濃くなったのを元に戻すCSS
Google Adsense
Social bookmark comment : 0
No comment.
Comment : 4
- http://kozy.heteml.jp/l4l/
2008-03-31 (月) 10:39 -
関数(スコープ)が終了しないとvar変数は開放されない気がします。
同じコードでFlex3のプロファイラで確認したところ問題なくGCされてましたよ。
あとnullもobjectという罠。
trace(typeof(null)); // object - katsuma.tv
2008-04-04 (金) 03:54 -
あーーなるほど。スコープが関数単位だから、その関数を抜け出さないと開放されないんですね。すごく納得。
ちなみにFlex3のプロファイラはFlexBuilderとかですか??>あとnullもobjectという罠。
これはひどい罠wしかしすごく勉強になりました。ありがとうございました!
- http://kozy.heteml.jp/l4l/
2008-04-04 (金) 10:05 -
>ちなみにFlex3のプロファイラはFlexBuilderとかですか??
そうですよ~。これがあれば、
1.クラス毎のこれまで生成したインスタンス
2.現在生成されているインスタンス
3.1.のメモリ割合
4.2.のメモリ割合
が分かってメモリリーク問題はかなり簡単に見抜けますよ。 - katsuma.tv
2008-04-05 (土) 13:15 -
>メモリリーク問題はかなり簡単に見抜けますよ。
うわあああ、やっぱめちゃめちゃ便利そうですね。。
会社におねだりしたいと思います><
Trackback : 0
- TrackBack URL for this entry
- http://blog.katsuma.tv/mt-tb.cgi/141
- Listed below are links to weblogs that reference
- FlashPlayer9のガベージコレクタのメモリ解放の考察 from blog.katsuma.tv
2008/03/29 (Sat)