cannot access class jdk.internal.ref.Cleaner (in module java.base) because module java.base does not export jdk.internal.ref to unnamed module

Uwe Schindler uschindler at
Sat Aug 6 17:59:59 UTC 2016


if you are interested, the Apache Lucene team has a implementation of the unmapper that's safe with any currently known Java version. The problem with using pure reflection is that you cannot be sure before actually calling the unmappinmg that it works. We use MethodHandles to work around that (early linking before actually calling). It uses Alan's Runnable trick.

We had this problem in Lucene 5 versions, which are broken (until 5.5.1) with recent Java 9, because we used reflection and we were not able to detect in a 100% safe way that the unmapping APIs are accessible and actually work. We changed the code in Lucene 6+ to use MethodHandles for early linking before trying to call anything, so there is no reflection involved at unmapping time (only an invokeExact).

This code does the lookup and inspection + building the MethodHandle (wrapped in AccessController): (Github)

The result is Lucene specific implementation of BufferCleaner (an interface), but it basically only calls a MethodHandle that unmaps the ByteBuffer. The MethodHandle also contains all NULL checks needed and can be safely called. The MethodHandle is here: (Github)

and invoked like that: (Github)

I hope that helps! Apache Lucene, Apaceh Solr, and Elasticsearch are using this in production since beginning of this year.


Uwe Schindler
uschindler at 
ASF Member, Apache Lucene PMC / Committer
Bremen, Germany
> -----Original Message-----
> From: jigsaw-dev [mailto:jigsaw-dev-bounces at] On Behalf
> Of Julien Gouesse
> Sent: Saturday, August 6, 2016 5:45 PM
> To: Julien Gouesse <gouessej at>; Alan Bateman
> <Alan.Bateman at>; jigsaw-dev at
> Subject: Re: cannot access class jdk.internal.ref.Cleaner (in module java.base)
> because module java.base does not export jdk.internal.ref to unnamed
> module
> You're right, jdk.internal.ref.Cleaner implements Runnable unlike
> sun.misc.Cleaner:
> a.base/share/classes/jdk/internal/ref/
> It works, I no longer need "--add-exports". Thank you very much.
> Le Samedi 6 août 2016 17h19, Julien Gouesse <gouessej at> a écrit :
> It doesn't work:
> [gouessej at localhost test-classes]$ ~/Téléchargements/jdk-9/bin/java -cp
> ../classes:../test-classes --add-exports java.base/jdk.internal.ref=ALL-
> UNNAMED engine/misc/TestDeallocationHelper
> Unrecognized option: --add-exports
> Error: Could not create the Java Virtual Machine.
> Error: A fatal exception has occurred. Program will exit.
> Ok I'm going to try to treat it as a Runnable.
> Le Samedi 6 août 2016 16h02, Alan Bateman <Alan.Bateman at> a
> écrit :
> On 06/08/2016 02:52, Julien Gouesse wrote:
> > Hi
> >
> >
> > I need to run the cleaner of a direct NIO byte buffer by using the reflection
> API to access to non public APIs as I successfully did with previous versions of
> Java. The cleaner is in the class "Cleaner" in the package "jdk.internal.ref" in
> the module "java.base" and my own module is unnamed. Then, I tried to
> pass "--add-exports java.base/jdk.internal.ref=ALL-UNNAMED" to the
> command "java" but I get the following exception:
> > [gouessej at localhost test-classes]$ ~/Téléchargements/jdk-9/bin/java -cp
> ../classes:../test-classes engine/misc/TestDeallocationHelper --add-exports
> java.base/jdk.internal.ref=ALL-UNNAMED
> You've put the --add-exports option after your main class on the command
> line, try place it before the main class so that it's considered as an
> argument to the java launcher rather an argument to your main class.
> One other thing to point out if that jdk.internal.ref.Cleaner is a
> Runnable so you can at least stay in the world of standard types.
> -Alan

More information about the jigsaw-dev mailing list