2010年8月22日日曜日

エスケープ解析とfinalize

ARMについて調べているときに、エスケープ解析でスタックに割り付けられたオブジェクトのfinalizeがちゃんと呼ばれるなら、確実なリソースの開放が出来るんじゃないかと思って調べてみた。

まずエスケープ解析でスタックに割り当てられるようなオブジェクトを作って動作を確認する。
その後、そのオブジェクトでfinalizeをoverrideしてみたところ、スタックに割り当てられなくなったっぽい。
(GCログで確認)

finalizeを実行するスレッドを確認すると、通常通りFinalizerスレッドで実行されていた。これだと、非同期に実行されるのでメソッドを抜けたときに開放できないし(finalizeが実行されるまで待つなら出来るが…)、Finalizerスレッドからfinalizeを呼び出すためにオブジェクトが漏れてしまっている。
Javaの理論と実践: ガベージコレクションとパフォーマンスにもそのようなことが書いてある。

エスケープ解析はJVMの最適化オプションであってVMが満たすべき仕様ではないからか、VM仕様に書かれてはいないようだが、エスケープ解析の論文を適当に幾つか読むと、finalizeをoverrideしていたらどうしても漏れる(Global Escape)から最初からエスケープ解析する対象から省いてしまうらしい。
ただ、java.lang.ref.Finalizer$FinalizerThreadを使うこと自体もSunのJVM実装仕様だったはずなので、他のVMだったら違うかもしれない。



2010年8月21日土曜日

Java7のjavac新機能を試した

jdk1.7.0 b105でARM(Automatic Resource Management/try-with-resources)が入ったので試してみた。
リソースを自動で閉じてくれるもので、Pythonとかのwithみたいな感じに使える。
ARMで自動で閉じられるようにするには、java.lang.AutoCloseableを実装している必要がある
ARMはclose/finallyより使う側の負荷が低いのでよいと思う。

理由

  • 使う側のコード量が少なくなる
  • コードが少なくなる理由でもあるが、複数のリソースを確実に閉じられる。

試しに作ってみたもの。マルチキャッチも使ってみた。

public class Test {
  private static class AC implements AutoCloseable {
    public void close() throws java.io.IOException {
      throw new java.io.IOException();
    }
  }

  private static class AC2 implements AutoCloseable {
    public void close() throws InterruptedException {
      throw new InterruptedException();
    }
  }

  public static void main(String... args) {
    try {
      try(AC ac = new AC(); AC2 ac2 = new AC2()) { }
    } catch (final java.io.IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }
}

これを実行すると

java.lang.InterruptedException
        at Test$AC2.close(Test.java:10)
        at Test.main(Test.java:16)
        Suppressed: java.io.IOException
                at Test$AC.close(Test.java:4)
                ... 1 more

AC1, AC2がそれぞれブロックを抜けるときのcloseで例外を投げるが、とりあえず全部closeされる。

ここでcloseが投げる例外が無ければ外側のtry/catchは不要だし、
例外に継承関係があれば(java.io.IOExceptionとjava.io.FileNotFoundExceptionとか)があれば親側でcatchするのでも可能。

追記
finallyの場合RuntimeExceptionでもfinallyは実行されるので、ARMではどうか確認したら、途中でRuntimeExceptionが起きた場合もちゃんと全部closeが呼び出された。
これならfinallyでRuntimeExceptionが発生して、全部後処理しきれないとかの問題は回避できるな。