Go to list of users who liked
More than 5 years have passed since last update.
Java9でsun.misc.Cleanerのpackageが移動してしまうことへの対処(Apache Hadoopの場合)
概要
JDK9では、JDK-8148117などによって、sun.misc.Cleaner
がjdf.internal.ref.Cleaner
に移動してしまった。JDK9だけで動かす前提なら単純にimport文を書き換えれば良いが、JDK9とJDK8の両方でビルドを通したい場合は黒魔術的な対処が必要になる。以下、この黒魔術について解説する。
目的
[HADOOP-12760]sun.misc.Cleaner has moved to a new location in OpenJDK 9をなんとかしたい
詳細
問題となっているコード
/** * Unmaps the block from memory. See munmap(2). * * There isn't any portable way to unmap a memory region in Java. * So we use the sun.nio method here. * Note that unmapping a memory region could cause crashes if code * continues to reference the unmapped code. However, if we don't * manually unmap the memory, we are dependent on the finalizer to * do it, and we have no idea when the finalizer will run. * * @param buffer The buffer to unmap. */publicstaticvoidmunmap(MappedByteBufferbuffer){if(bufferinstanceofsun.nio.ch.DirectBuffer){sun.misc.Cleanercleaner=((sun.nio.ch.DirectBuffer)buffer).cleaner();cleaner.clean();}}
どうでもいいコメント
mlockはCのコードを呼び出しているのにmunmapがそうしていないのは謎
- 過去の経緯(HDFS-4953)を読むと、JNIへの依存を避けてSun-specific APIを使うことにしたようだが、他のコードはガンガンJNIを使っているので、一貫性がない
- (追記) munmapはWindowsで動作させられないので、Javaで書いている
JDK9ではsun.misc.Cleaner
が移動してしまっているので、このコードは動かない。そのため、以下のようなコードで対応する。
privatestaticMethodHandlecleanMethod=null;static{MethodHandles.Lookuplookup=MethodHandles.lookup();try{finalClass<?>directBufferClass=Class.forName("java.nio.DirectByteBuffer");finalMethodm=directBufferClass.getMethod("cleaner");// Set permission to the lookup class for making// a direct method handlem.setAccessible(true);// Make a direct method handleMethodHandledirectbufferCleanerMethod=lookup.unreflect(m);// Get the return type of java.nio.DirectByteBuffer// JDK8: sun.misc.Cleaner// JDK9: jdk.internal.ref.CleanerClass<?>cleanerClass=directbufferCleanerMethod.type().returnType();// Get the method (Cleaner#clean)cleanMethod=lookup.findVirtual(cleanerClass,"clean",methodType(void.class));}catch(NoSuchMethodException|IllegalAccessException|ClassNotFoundExceptionignored){}}
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/07dcdd5d9401 によると、java.nio.DirectByteBuffer#cleaner
の戻り値が
- JDK8:
sun.misc.Cleaner
- JDK9:
jdk.internal.ref.Cleaner
になっているので、これを呼び出している。munmapメソッドは以下のように書き換えられる。
publicstaticvoidmunmap(MappedByteBufferbuffer){if(bufferinstanceofsun.nio.ch.DirectBuffer){if(cleanMethod!=null){try{// Execute Cleaner#cleancleanMethod.invokeExact(((sun.nio.ch.DirectBuffer)buffer).cleaner());}catch(Throwableignored){}}
Class.forName
でsun.misc.Cleaner
もしくはjdk.internal.ref.Cleaner
を直接呼び出してもいいのでは、という案もあるが、jdk.internal.ref.Cleaner
がいつまで居座っているかもわからないのでこういった手法をとっている。
参考
同様の手法(黒魔術)は、Apache Luceneで既に実施されており、本記事はこれに基づいたものである。
- Apache Luceneにおける対処:MMapDirectory.java (GitHub)
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme