Premise: I have had 2 classes loaded by different class loaders - one from the app classloader and the other, a custom class loader. The class loaded by the custom class loader is trying to reflectively access a private field in the class loaded via the appclass loader. In this case, I get a runtime error along the lines of "Failed to access class A from class B; A is in unnamed module of loader 'app' and B is in unnamed module of loader 'Custom'
. The error goes away after adding an '--add-opens java.base/java.lang=ALL-UNNAMED'
Questions : I understand that there can be up to one unnamed module per classloader in the VM.
What part of the JLS or JDK implementation specific to hotspot VM talks about interactions between two unnamed modules across loaders ? I can see a lot of unnamed to named module interactions etc in the document, but not between two unnamed.
I understand why add-opens is solving the issue in general( the code is being invoked reflectively and end calls into JDK happen via java.lang.reflect APIs?). However, unclear again as to how the add-opens works across loaders - does
--add-opens=ALL-UNNAMED
expected to open the packages in the specified module to all unnamed modules across loaders in the VM or only to that loader?
Using Java 17 + hotspot VM.
After a lot of trial and error, I finally got a reproducer - but I had to make quite a few assumptions.
Reproducer:
Assumptions:
A
is NOTpublic
This is supported by the error message - because nowA
andB
are in different runtime packages,B
can't access a non-public class in the other runtime package. The error message matches.B
referencesA
directly. Reflective access (Class.forName
) would still succeed.ClassLoader
you (or the library you use) first tries to useClassLoader.defineClass
. This would explain why adding--add-opens java.base/java.lang=ALL-UNNAMED
would exhibit different behavior. (Not an uncommon thingRunning this code yields:
while running it with
--add-opens java.base/java.lang=ALL-UNNAMED
yieldsI already hinted in my assumptions about the cause for this:
A
andB
are in different runtime packages, andA
is not public - which meansA
is not accessible toB
.This has nothing to do with modules.
To answer your other questions:
(Emphasis mine)
No,
java.lang
andjava.lang.reflect
are two different packages. They have nothing to do with each other - except that they are in the same module, part of the platform...It is more likely that you or a library that you use first tries to hack into
java.lang.ClassLoader.defineClass
- which resides in thejava.lang
package - and falls back to creating their ownClassLoader
.Good question.
ALL-UNNAMED
implies ALL unnamed modules.We can test this with this code (and reusing the
CustomClassLoader
from the reproducer:When we run this code with
--add-opens java.base/java.lang=ALL-UNNAMED
this yields:So the answer is: Yes,
ALL-UNNAMED
opens the package to all unnamed modules, for everyClassLoader
.