We have an application with custom classloader and I need to access classLoader field on given classes. However this field is not accessible via reflection :-(
The JavaDoc for java.lang.Class is clear:
// This field is filtered from reflection access, i.e. getDeclaredField
// will throw NoSuchFieldException
So this is what I get when calling getDeclaredField("classLoader")
Can this be obtained somehow (I see IntelliJ debugging does that somehow; how?)
Maybe some byteBuddy trickery?
To answer your question: You can still hack your way to the field using Byte Buddy by creating a mirror of the class which will have a smiliar class layout such that you can access and modify fields using Unsafe by first creating a mirror of the class that hides fields from reflection:
Class<?> mirror = new ByteBuddy()
.with(TypeValidation.DISABLED)
.redefine(Class.class)
.name("mirror.Class")
.noNestMate()
.make()
.load(null)
.getLoaded();
Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Object unsafe = theUnsafe.get(null);
long offset = (Long) unsafeType
.getMethod("objectFieldOffset", Field.class)
.invoke(unsafe, mirror.getDeclaredField("classLoader"));
ClassLoader loader = (ClassLoader) unsafeType
.getMethod("getObject", Object.class, long.class)
.invoke(unsafe, Foo.class, offset - 4);
The mirror has a smiliar field layout as the original class such that you can retain that layout and access fields as you demand it. You can in the same way use putObject to override the field value.
Would I recommend this approach however? Absolutely not. This will stop working in any future version of Java, too. If you need some extra time to work on a proper solution, this might be a way to go but long term, you should refactor your code to make this work-around uneccessary.
Java debuggers see this field, because they rely on JDWP, which works on top of native APIs: JNI and JVM TI.
You technically can access/modify
classLoaderfield with JNI or Unsafe, but please don't do this!
After all, why do you think this field is filtered from reflection access? Exactly to prevent people from shooting themselves in the foot by modifying the field.
The key point is that a class should never be separated from its class loader. HotSpot JVM cannot unload classes one by one; instead, it unloads the whole class loader, when no live references to the ClassLoader object remain.
When a ClassLoader object is garbage collected, the corresponding part of the Metaspace can be reclaimed, along with the metadata for all classes loaded by this ClassLoader.
Now, what happens if you null out classLoader field? If there are no more references to the corresponding ClassLoader, it becomes eligible for garbage collection (seems like exactly what you want). But this may trigger class unloading, that will kill all the metadata for classes of this loader. However, classes (and their instances) are completely broken without the metadata. After that, any operation on your class or one of its instances may randomly crash JVM or make the application otherwise unstable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With