@SuppressForbidden(reason = "Needs access to private APIs in DirectBuffer and sun.misc.Cleaner to enable hack")
  private static Object unmapHackImpl() {
    final Lookup lookup = lookup();
    try {
      final Class<?> directBufferClass = Class.forName("java.nio.DirectByteBuffer");
      
      final Method m = directBufferClass.getMethod("cleaner");
      m.setAccessible(true);
      MethodHandle directBufferCleanerMethod = lookup.unreflect(m);
      Class<?> cleanerClass = directBufferCleanerMethod.type().returnType();
      
      final MethodHandle cleanMethod;
      if (Runnable.class.isAssignableFrom(cleanerClass)) {
        // early Java 9 impl using Runnable (we do the security check early that the Runnable does at runtime):
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
          sm.checkPackageAccess("jdk.internal.ref");
        }
        // cast return value of cleaner() to Runnable:
        directBufferCleanerMethod = directBufferCleanerMethod.asType(directBufferCleanerMethod.type().changeReturnType(Runnable.class));
        cleanerClass = Runnable.class;
        // lookup run() method on the interface instead of Cleaner:
        cleanMethod = lookup.findVirtual(cleanerClass, "run", methodType(void.class));
      } else {
        // can be either the old internal "sun.misc.Cleaner" or
        // the new Java 9 "java.lang.ref.Cleaner$Cleanable":
        cleanMethod = lookup.findVirtual(cleanerClass, "clean", methodType(void.class));
      }
      
      final MethodHandle nonNullTest = lookup.findStatic(Objects.class, "nonNull", methodType(boolean.class, Object.class))
          .asType(methodType(boolean.class, cleanerClass));
      final MethodHandle noop = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, cleanerClass);
      final MethodHandle unmapper = filterReturnValue(directBufferCleanerMethod, guardWithTest(nonNullTest, cleanMethod, noop))
          .asType(methodType(void.class, ByteBuffer.class));
      
      return (BufferCleaner) (ByteBufferIndexInput parent, ByteBuffer buffer) -> {
        if (directBufferClass.isInstance(buffer)) {
          final Throwable error = AccessController.doPrivileged((PrivilegedAction<Throwable>) () -> {
            try {
              unmapper.invokeExact(buffer);
              return null;
            } catch (Throwable t) {
              return t;
            }
          });
          if (error != null) {
            throw new IOException("Unable to unmap the mapped buffer: " + parent.toString(), error);
          }
        }
      };
    } catch (ReflectiveOperationException e) {
      return "Unmapping is not supported on this platform, because internal Java APIs are not compatible to this Lucene version: " + e; 
    } catch (SecurityException e) {
      return "Unmapping is not supported, because not all required permissions are given to the Lucene JAR file: " + e +
          " [Please grant at least the following permissions: RuntimePermission(\"accessClassInPackage.sun.misc\"), " +
          "RuntimePermission(\"accessClassInPackage.jdk.internal.ref\"), and " +
          "ReflectPermission(\"suppressAccessChecks\")]";
    }
  }