diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index c3f305d97a7..fc95bb2aad3 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -61,6 +61,9 @@ New Features
* LUCENE-6975: Add ExactPointQuery, to match a single N-dimensional
point (Robert Muir, Mike McCandless)
+* LUCENE-6989: Add preliminary support for MMapDirectory unmapping in Java 9.
+ (Uwe Schindler, Chris Hegarty, Peter Levart)
+
API Changes
* LUCENE-6067: Accountable.getChildResources has a default
diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
index 8886ab12730..57ce3ddea6d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
@@ -1055,7 +1055,11 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
"index=" + segString() + "\n" +
"version=" + Version.LATEST.toString() + "\n" +
config.toString());
- infoStream.message("IW", "MMapDirectory.UNMAP_SUPPORTED=" + MMapDirectory.UNMAP_SUPPORTED);
+ final StringBuilder unmapInfo = new StringBuilder(Boolean.toString(MMapDirectory.UNMAP_SUPPORTED));
+ if (!MMapDirectory.UNMAP_SUPPORTED) {
+ unmapInfo.append(" (").append(MMapDirectory.UNMAP_NOT_SUPPORTED_REASON).append(")");
+ }
+ infoStream.message("IW", "MMapDirectory.UNMAP_SUPPORTED=" + unmapInfo);
}
}
diff --git a/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java b/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
index dca843de6cc..da8cf704aa8 100644
--- a/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
+++ b/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
@@ -17,6 +17,9 @@
package org.apache.lucene.store;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.methodType;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
@@ -27,10 +30,10 @@ import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.Future;
+import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import org.apache.lucene.store.ByteBufferIndexInput.BufferCleaner;
@@ -160,24 +163,6 @@ public class MMapDirectory extends FSDirectory {
assert this.chunkSizePower >= 0 && this.chunkSizePower <= 30;
}
- /**
- * true
, if this platform supports unmapping mmapped files.
- */
- public static final boolean UNMAP_SUPPORTED =
- AccessController.doPrivileged((PrivilegedAction) MMapDirectory::checkUnmapSupported);
-
- @SuppressForbidden(reason = "Java 9 Jigsaw whitelists access to sun.misc.Cleaner, so setAccessible works")
- private static boolean checkUnmapSupported() {
- try {
- Class> clazz = Class.forName("java.nio.DirectByteBuffer");
- Method method = clazz.getMethod("cleaner");
- method.setAccessible(true);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
/**
* This method enables the workaround for unmapping the buffers
* from address space after closing {@link IndexInput}, that is
@@ -191,8 +176,9 @@ public class MMapDirectory extends FSDirectory {
* is false
and the workaround cannot be enabled.
*/
public void setUseUnmap(final boolean useUnmapHack) {
- if (useUnmapHack && !UNMAP_SUPPORTED)
- throw new IllegalArgumentException("Unmap hack not supported on this platform!");
+ if (useUnmapHack && !UNMAP_SUPPORTED) {
+ throw new IllegalArgumentException(UNMAP_NOT_SUPPORTED_REASON);
+ }
this.useUnmapHack=useUnmapHack;
}
@@ -310,23 +296,87 @@ public class MMapDirectory extends FSDirectory {
return newIoe;
}
- private static final BufferCleaner CLEANER = (final ByteBufferIndexInput parent, final ByteBuffer buffer) -> {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction() {
- @Override
- @SuppressForbidden(reason = "Java 9 Jigsaw whitelists access to sun.misc.Cleaner, so setAccessible works")
- public Void run() throws Exception {
- final Method getCleanerMethod = buffer.getClass().getMethod("cleaner");
- getCleanerMethod.setAccessible(true);
- final Object cleaner = getCleanerMethod.invoke(buffer);
- if (cleaner != null) {
- cleaner.getClass().getMethod("clean").invoke(cleaner);
- }
- return null;
- }
- });
- } catch (PrivilegedActionException e) {
- throw new IOException("Unable to unmap the mapped buffer: " + parent.toString(), e.getCause());
+ /**
+ * true
, if this platform supports unmapping mmapped files.
+ */
+ public static final boolean UNMAP_SUPPORTED;
+
+ /**
+ * if {@link #UNMAP_SUPPORTED} is {@code false}, this contains the reason why unmapping is not supported.
+ */
+ public static final String UNMAP_NOT_SUPPORTED_REASON;
+
+ /** Reference to a BufferCleaner that does unmapping; {@code null} if not supported. */
+ private static final BufferCleaner CLEANER;
+
+ static {
+ final Object hack = AccessController.doPrivileged((PrivilegedAction