Progressionでガベージコレクションされないのをどうにかしようとした記録
- 2009 年 8月 7 日
Progressionがとにかく便利でよく使っているのですがメモリーまわりにはずーっと悩まされている。
特に困っているのがProgressionのオブジェクトを含む外部swfを読み込むとunloadしてもメモリーに残ったまま消えない。
外部swfでもProgressionのオブジェクトを使いたいのだけれど今のところこの事情で外部swfには思うようにProgressionのクラスを使えていないです。
参考
CastSprite内でCastImageLoaderを使用するとメモリが解放されない?
LoadChildした外部swfを完全にUnloadするには(そういえば質問していたなぁ。)
Astronika blog – Progression、メモリリークについての個人的メモ
消えない状態はこんな感じ。
読み込まれるサンプルextra.swfは画像を一杯おいてファイルサイズをわざと大きくした。
そしてCastSprteを継承したドキュメントクラスにして、ついでにシーンとかコマンドのインスタンスも宣言。
load、unloadでどんどん増えてくよー。
extra.swf
package{ import flash.display.Sprite; import jp.progression.casts.CastSprite; import jp.progression.scenes.SceneObject; import jp.progression.core.commands.Command; import jp.progression.commands.Trace; import flash.events.MouseEvent; public class Extra extends CastSprite{ public function Extra(initObject:Object=null){ super(initObject); this.scene = new SceneObject( "sample", { name:"extra", group:"extra" } ); this.scene.onGoto = function():void{ trace( "sample" ) }; this.comm = new Trace( "trace", { id:"extra", name:"extra", group:"extra" } ); this.comm.after( function():void{ trace("sample") } ) this.addEventListener( MouseEvent.MOUSE_DOWN, this.mouseDownHandler, false, 0, true ); } public var comm:Trace; public var scene:SceneObject; protected override function _onCastRemoved():void{ } public function mouseDownHandler(e:MouseEvent):void{ trace( "sample" ) } } } |
サンプル:Progressionオブジェクトを持つswfを読み込むと…
ちなみにドキュメントクラスがMovieClipとかFlashの標準オブジェクトでも、CastObjectとかProgressionのオブジェクトがあると同じく消えないです。
なんとかならぬものかと、色々調べていたらwonderflでそれらしき記事を発見
参照が消えていないっていうのはわかっておりましたが、具体的な箇所まで調べられております。
だったらと色々やってみたところ、メモリーを消すってところまではなんとかなりました。無茶苦茶な方法で。
wonderflの記事によるとstaticなDictionaryにProgressionのオブジェクトがキーなり値なりでコレクションされているのが原因のようなので、僕もProgressionのクラスを眺めてみたのだけれどそれらしき箇所を確認。特にインスタンス生成時に追加され続けるDictionaryは上書きで消えることもないのでメモリーにひらすら溜まっていくって状況になる模様。
問題になりそうなのはwonderflにある、
jp.nium.core.collections.ExDisplayCollection
jp.nium.display.ChildIndexer
のDictionaryと
jp.progression.core.collections.*
の各クラスDictionary。
これらの中のDictionaryはすべてstatic privateが使われていて外からはどうにも出来そうに無かったので、とりあえず全部publicに書き換えて保存。swfをunloadしたら全部のキーを削除しちゃえばメモリーは解放されるかもと強引にやってみた。
Dictionayを全部消しちゃうメソッド作って、unload後実行してみたら…。
消すメソッド
public class ClearCollection{ public function ClearCollection(){ throw new Error( "error" ); } static public function clearDictionary():void{ clearDictionaryKey( ExDisplayCollection._groups ); clearDictionaryKey( ExDisplayCollection._ids ); clearDictionaryKey( ExDisplayCollection._instances ); clearDictionaryKey( ExDisplayCollection._nums ); clearDictionaryKey( ChildIndexer._indexers ); clearDictionaryKey( CommandCollection._groups ); clearDictionaryKey( CommandCollection._ids ); clearDictionaryKey( CommandCollection._instances ); clearDictionaryKey( CommandCollection._nums ); clearDictionaryKey( SceneCollection._groups ); clearDictionaryKey( SceneCollection._ids ); clearDictionaryKey( SceneCollection._instances ); clearDictionaryKey( SceneCollection._nums ); clearDictionaryKey( CommandCollection._ids ); clearDictionaryKey( CommandCollection._instances ); clearDictionaryKey( CommandCollection._nums ); } static public function clearDictionaryKey( dic:Dictionary ):void{ for( var i:* in dic ){ delete dic[i]; } } } |
消えてる!
これで消えるには消える。
だけど、今のままだと消えて欲しくないものも消える。getInstanceByIdで参照したくて、id振ったCastObjectとかが。
外部swfの方のオブジェクトだけを判定して消せば、問題は少なくなりそうだけれどどうやって判定するよって感じ。
loaderInfoが行けるかと思ったけどComandやSceneが判定できなかったし。
Progression自体に手を入れないでやるなら、オブジェクト全部group名同じに設定するとか?
オブジェクト作る度に消すように記録しておくとかか?
そもそも消していいの?みたいな…。(w
Progressionのクラス自体を変更して他にどんなトラブルが発生するかわからないし。
仕事で納品直前になって、もし、これに起因する大トラブルがあったら?なんて考えたらとても踏み切れません。誰かいい方法があったら教えてください。
ということで、結論としては駄目でした。次期Progressionはこのあたりをいい感じにしてくれそう。待ちましょう。
Progressionフレームワーク開発者が語る、
Progressionの魅力とFlashクリエイターの理想像
「ファイル単体でも動作し、別ファイルとつなげても動作するものを設定を変えずに実装」ってワクワクする。心待ちにしています。