FindLoadedClass () returns null

According to the JVM specification, the class loader that initiates the loading of the class is recorded as the initiating JVM class loader. Also, according to the JavaDoc ClassLoader # findLoadedClass () method

Returns a class with the given binary name if this loader was written by the Java virtual machine as the initiator of loading the class with this binary name.

(my accent)

Consider a simple class loader

class SimpleClassLoader extends ClassLoader { void foo() { System.err.println(loadClass("foo.Bar")); System.err.println(findLoadedClass("foo.Bar")); } } 

Given that foo.Bar really exists in the class path, new SimpleClassLoader().foo() prints

 class foo.Bar null 

For the reasons given above, SimpleClassLoader should be the initiating class loader, and findLoadedClass("foo.Bar") should simply return a successfully loaded class.

Now consider this second version:

 class SimpleClassLoader2 extends ClassLoader { SimpleClassLoader2() { super(null); // disables delegation } protected Class<?> findClass(String name) { try { byte[] b = IOUtils.toByteArray(new FileInputStream("path/to/foo/Bar.class")); return defineClass("foo.Bar", b, 0, b.length); } catch (Exception e) { e.printStackTrace(); return null; } } void foo() { System.err.println(loadClass("foo.Bar")); System.err.println(findLoadedClass("foo.Bar")); } } 

This makes SimpleClassLoader2 both the initiating and the defining class loader foo.Bar . Indeed, now new SimpleClassLoader2().foo() prints the desired

 class foo.Bar class foo.Bar 

Thus, either the documentation is incorrect, or I do not understand why SimpleClassLoader not considered as the initial loader of the foo.Bar classes. Can someone shed some light on this?

+8
java internals jvm
source share
1 answer

I did some more tests, and I'm sure the spec is implemented correctly. My mistake was that the reflective loading of the class is the same as loading it as part of the resolution step. This makes sense: both the specification and the JavaDoc mention the "record" of the class loader as the starting class loader. If I call loadClass() myself, the VM has no way of knowing which classloader should be the initiating classloader, so the defining classloader also trivially becomes the classloader.

This can be demonstrated by loading a loaded class with a trigger of another class ( foo.Baz ) as part of resolving dependencies, but loading another class loader actually loads it. *

* I am sure that this is the wrong behavior of a valid class loader. I just do it to illustrate the point.

Consider the following classes (they are all in the foo package):

 public class Bar { public Bar() { new Baz(); } } 

and

 public class Baz { } 

My custom classloader is now slightly modified:

 public class SimpleClassLoader extends ClassLoader { static final String PATH = "/path/to/classes"; public SimpleClassLoader() { // disable parent delegation super(null); } public void printLoadedClass(String name) throws Exception { Class<?> cls = findLoadedClass(name); System.err.println("findLoadedClass(" + name + ") = " + cls + ", has class loader " + cls.getClassLoader()); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (name.equals("foo.Baz")) { // don't want to be defining class loader of foo.Baz return getSystemClassLoader().loadClass(name); } // now we're loading foo.Bar try { byte[] b = IOUtils.toByteArray(new FileInputStream(PATH + "/foo/Bar.class")); return defineClass(name, b, 0, b.length); } catch (ClassFormatError | IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } } 

The test goes straight:

 public static void main(String[] args) throws Exception { SimpleClassLoader cl = new SimpleClassLoader(); Class<?> cls = cl.loadClass("foo.Bar"); cls.newInstance(); // this triggers resolution cl.printLoadedClass("foo.Bar"); cl.printLoadedClass("foo.Baz"); } 

Exit

 findLoadedClass(foo.Bar) = class foo.Bar, has class loader foo.SimpleClassLoader@3a65724d findLoadedClass(foo.Baz) = class foo.Baz, has class loader sun.misc.Launcher$AppClassLoader@1a2b2cf8 

As you can see: SimpleClassLoader initiates the download and also defines foo.Bar . Creating an instance triggers the foo.Baz permission. This time the class definition is delegated to the system class loader, so it becomes the defining class loader. The output shows that SimpleClassLoader launches the class loader for both classes, but only defines the first class.

+3
source share

All Articles