イベントリスナ
Javaでのメモリリークの原因としてありがちなのが、イベントリスナとして登録しっぱなしになっていて解放されないというのがあります。
class View implements HogeListener { public View( Model model ) { model.addHogeListener( listener ); } ... } class Model { public void addHogeListener( HogeListener listener ) { ... } ... } public class Main { public static void main( String[] ) { Model model = new Model(); // モデルに対して繰り返して処理を行う while( true ) { View view = new View( model ); view.show(); } } }
↑たとえばこんなコードとか。JTable と TableModel の関係とかに似ていると思う。
上記のようなコードの場合、ViewのコンストラクタでモデルのaddHogeListenerを呼び出して自身をリスナとして登録しているわけですが、removeListenerされていないんですよね。
その結果、modelがGC対象になるまでViewのオブジェクトもGC対象にならないということになります。
上のようなコードならMainクラスのwhileループ内でaddHogeListenerとremoveHogeListenerを呼び出せばよいけど、そうもいかないことの方が多いですね。
そこでちょっと思いついたのですが、リスナを保持するときにWeakReferenceで保持したらそういう心配はいらなくなるんじゃないかと。
#ただし、リスナとしてのみ参照が存在するオブジェクトの場合はGC対象にされてしまうので使えないけどね。
上記の例のばあい、Mainクラスのwhileループの終端に処理が達したときに view がスコープ外になり、modelに登録しているリスナとしての参照以外は参照が存在しなくなります。そしてリスナとしての参照は WeakReference による参照のためリスナオブジェクト=viewが参照してたオブジェクトはGC対象になります。
この処理の主体はリスナとしての処理ではなくViewとしての処理なんで、viewがスコープ外になったときにそのオブジェクトはもう必要ないわけです。リスナをWeakReferenceで保持することで、viewのオブジェクトが必要なくなったときにGC対象にすることが可能になります。
全てのリスナをWeakReferenceとした方が良いというわけではなく、そのへんはケースバイケースかなと。WeakReferenceの参照として登録するか否かはなんらかのマーカインタフェースを導入するとか addXXXXListener メソッドに引数を増やすとかで使いわければいいかなと思います。
#ただ、この仕組みを利用するにはオブジェクト間の参照に関してある程度の知識が必要になると思うので、ある程度の使用パターンがわからないと使いづらいかも。間違って使用すると、リスナとして登録されててほしいのにGC対象にされちゃったりとかあると思うんで。