Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access classLoader field via reflection

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?

like image 741
kosta5 Avatar asked Jul 11 '26 02:07

kosta5


2 Answers

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.

like image 185
Rafael Winterhalter Avatar answered Jul 13 '26 14:07

Rafael Winterhalter


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 classLoader field 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.

like image 23
apangin Avatar answered Jul 13 '26 16:07

apangin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!