finalizeパターン

というものがあるのかどうか知らないが、主にjavavm管轄外リソースの解放の仕方。

public class ResourceClass {

  private Object resource;

  public getResource() {
    if (resource == null) {
      resource = createResource();
    }
    return resource;
  }

  public void close() {
    // 何らかの解放処理
    resource = null;
  }

}

みたいなクラスがあったとして、resourceの解放をどうするか?
使い終わったらclose()してくださいね。とjavadocに書いておくにしても使う人が守るとは限らない。
close()が呼ばれなくても解放するには?

public class ResourceClass {

  private static final List<WeakReference<ResourceClass>> someResources = Collections.synchronizedList(new ArrayList<WeakReference<ResourceClass>>());

  static {
    // vm終了時にgcされていないインスタンスの解放処理を呼ぶ
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        while (!someResources.isEmpty()) {
          ResourceClass resource = someResources.remove(0).get();
          // gcされていれば無視
          if (resource == null) {
            continue;
          }
          try {
            resource.innerClose();
          } catch (Throwable t) {
            // ログ出力など。再throwはしない。
          }
        }
      }
    });
  }

  // インスタンスを弱参照するオブジェクト
  private WeakReference<ResourceClass> ref = new WeakReference<ResourceClass>(this);
  // ファイナライズガーディアン
  @SuppressWarnings("unused")
  private Object finalizer = new Object() {
    @Override
    protected void finalize() throws Throwable {
      try {
        innerClose();
      } catch (Throwable t) {
        // ログ出力など。再throwはしない。
      }
      super.finalize();
    }
  };
  private Object resource;

  public ResourceClass() {
    someResources.add(ref);
  }

  private synchronized void innerClose() {
    someResources.remove(ref);
    // 何らかの解放処理
    resource = null;
  }

  public void close() {
    innerClose();
  }

}

みたいな。


ポイントは
・ファイナライズガーディアン
 普通にfinalize()をオーバーライドするとサブクラスで更にオーバーライドされるかも知れず、解放処理が実行されるとは限らない。
・同様の理由から実際に解放処理をするinnerClose()はprivateにする。
・finalize()は必ず呼ばれるとは限らないので、シャットダウンフックを使用する。
・ファイナライズ処理は複数のスレッドで動作することがあるようなので、スレッドセーフ&繰り返し呼ばれても平気なように実装する。
あたりか。


もちろんこのパターンが正しいかどうかはわからない。
そもそもファイナライズの使用は推奨されていないし。
ファイナライズとReferenceが絡むと微妙な同期問題があるらしいし。
http://japan.internet.com/developer/20060404/26.html


でもねぇ、いろいろライブラリ的なもの作ってるとその中で一時ファイルを作らざるを得ないこともままあるわけで、close()メソッドとかで削除するにしても必ず呼ばれるって保証が無いとその内ディスク使い切ったりしたら大事なわけで・・・