« KAG3 スクリプト文法まとめ | メイン | セーブフォルダとライセンス表示 »
2012年06月24日
吉里吉里Java:: メモリ管理とinvalidateスレッドの問題
Tweet @jin1016をフォローオリジナルの吉里吉里2は、リファレンスカウント方式によってメモリ管理を行っているが、吉里吉里Java では Java の GC に頼ったメモリ管理を行っている。
Java の GC に頼るとリファレンスカウント方式の循環参照の回避などメリットがあるが、いくつかの問題とオリジナルと異なる挙動が発生すると言う問題がある。
オリジナルはリファレンスカウントなので、カウントが消えた瞬間に解放されるが、Java の GC の場合はJVM の GC タイミング依存になり、オリジナルとは異なったタイミングでオブジェクトが消える。
TJS2 で参照が消えるのに任せた組み方をしていると、このタイミングの問題が顕在化する事がある。
顕在化するのはレイヤークラスくらいではないかと思っているが、他のクラスでも起こりうる。
レイヤーの場合、ネイティブインスタンスの解放タイミングが異なるためにしばらく親子関係が解消されず、レイヤー階層に参照されなくなったレイヤーが残る事になる。
不要になったときに適切に invalidate していれば、このタイミング依存の問題に遭遇する事はない。
TJS2 の仕様上は、オブジェクトがいつ解放されるかは不定となっているので、仕様は満たせているが、使い方によっては異なる挙動をする事があるので少し注意した方がいいかもしれない。
まあ、レイヤーのようなある程度重いクラスを何度も生成して放置すると言う使い方をする事自体稀だ思うが。
Java の GC はと言うか、finalize メソッドは Finalizer スレッドでコールされる。
Finalizer スレッドは、TJS2 VM とは異なるスレッドでかつ TJS2/吉里吉里2 がマルチスレッドで動作する事を考慮されていないため、Finalizer スレッドで TJS2 オブジェクトの invalidate 処理をすると問題が発生する。
TJS2/吉里吉里2 をリエントラント可能な処理に書き換えるのはコストに合わない。
オブジェクトは GC によって破棄される時だけ別スレッドで、他の処理はシングルスレッドで動作しているため、その時のためにロック処理するのは無駄な負荷が増えると考えられる。
シングルスレッドで動作させるためには、finalize メソッドで invalidate 処理をキューにいれ、TJS2 VM スレッドでそのキューに入った invalidate 処理を実行することになる。
リファレンスカウント方式だとカウントがなくなった時点、つまり TJS2 の処理の途中に解放処理が挟まる事になるが、invalidate 処理をキューにいれて処理をする場合、TJS2 のメソッド呼び出しがすべて返って来るまで処理できない。
つまり、1回のメソッド呼び出しでクラス生成を行ってそのクラスの参照が切れる処理を何度もするとメモリがどんどん消費されてメモリ不足に陥る。
ただ、ゲームのような用途でそのような処理を書く事はないと思われるので、問題は顕在化しないと考えられるが、バッチ処理を書いた場合ははまる可能性が高い。
吉里吉里2 でバッチ処理を書く場合は、現時点だと PC が対象だと思うので、メモリには余裕があるし、ネイティブで動作するオリジナルを使った方がいいため問題にはならいなと思うが。
メモリ管理方法の違いによって発生する問題で認識しているものはこの2つ。
投稿者 Takenori : 2012年06月24日 23:57
comments powered by Disqus