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

Julien Gouesse gouessej at
Sun Aug 7 22:35:39 UTC 2016

I assume that the similarities in the variable names are just some coincidences ;) (I'm obviously joking) :truly unusual experience of revolution / Code / [r1279] /pre_beta/src/main/java/engine/misc/
|   |
|   |   |   |   |   |
| truly unusual experience of revolution / Code / [r1279] /pre_beta/src/main/java/engine/misc/Dealloca.../** * Copyright (c) 2006-2016 Julien Gouesse This program is free software; you can * redistribute it and/or modify it under the terms of the GNU General Public  |
|  |
| Afficher sur | Aperçu par Yahoo |
|  |
|   |

I'll study your source code and quote it on StackOverflow. It looks clever, good job :) isAssignableFrom() is smarter than Arrays.asList(cleanerClass.getInterfaces()).contains(Runnable.class). java.lang.invoke is available since Java 1.7, I have to keep it in mind. As far as I know, my source code would go on working even with java.lang.ref.Cleaner$Cleanable.

    Le Samedi 6 août 2016 20h00, Uwe Schindler <uschindler at> a écrit :


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