以下の考察は openjdk のコードを元にしているため、oracle だとまた違うかもしれない。
map0 でめでたく native からヒープをがさっと取ってこれた場合、色々あった末に DirectBuffer を implements した DirectByteBuffer とやらが具象クラスとなる。 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/nio/DirectByteBuffer.java DirectBuffer は自身の確保したリソースを開放する役割を持つオブジェクト Cleaner を返すメソッドを持っており、clean の処理は Unmapper に委譲される。 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/nio/ch/FileChannelImpl.java#FileChannelImpl.Unmapper Cleaner は「オブジェクトが phantom reachable になった際に後処理を行いたい場合に利用するクラス」であり、ぶっちゃけ普通は出番ない。 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/Cleaner.java
ここから推測。
恐らく Cleaner を利用したのは lifetime の観点からみて、native ヒープから取ってきたバッファも vm ヒープから取ってきたバッファも、同じになるようにしたかったのではないかと考えられる。 finalize で munmap すると lifetime の観点からみて同じにはならない。オブジェクトは GC によって回収済みだけれど、ファイナライズ待ち、という状態が生まれるため。 これのモチベーションというかメリットは finalize で munmap する場合、ファイナライザキューにオブジェクトが積まれた後、ファイナライザが finalize を処理するまで munmap が遅れてしまうのを避けることができる、というところだと思う。前提条件として、対照のオブジェクトが finalize メソッドを実装していない(ため、ファイナライザキューには積まれない)、が必要。 今のところ他に解釈を思いつかない…
というわけで、可及的速やかに munmap が行われるように実装されてはいるのだけれども、とはいえ GC がオブジェクトを回収するタイミングの予測などは不可能なので、今回ようなありがちなアドホックなコードが結局書かれてしまうのだった…という話だと思う。 https://gist.github.com/frsyuki/6fce6a75a0c400740dd3 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/nio/ch/FileChannelImpl.java#790
結論としては、特に対処方法はなし(ワー)
追記:対処法()https://twitter.com/lyrical_logical/status/319579499794661376