Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a classloader load classes reference in the manifest classpath?

I used maven to built a jar with an external classpath additions using addClasspath.

When I run that jar using java -jar artifact.jar it is able to load classes from that main jar and from all jars in the libs directory.

However if I ask the system property java.class.path it will only list the main jar. If I ask the system class loader for its urls (ClassLoader.getSystemClassLoader().getURLs()) it will also only return the main jar. If I ask any class contained in some library for its class loader it will return the system class loader.

How is the system class loader able to load those classes?

It has to have some knowledge about those libraries in order to load classes from those. Is there a way to ask it for this kind of "extended" classpath?

like image 793
michas Avatar asked Sep 08 '14 16:09

michas


People also ask

How do I know what ClassLoader loads a class?

To know the ClassLoader that loads a class the getClassLoader() method is used. All classes are loaded based on their names and if any of these classes are not found then it returns a NoClassDefFoundError or ClassNotFoundException.

How are JVM classes loaded?

In order to actually load a class, the JVM uses Classloader objects. Every already loaded class contains a reference to its class loader, and that class loader is used to load all the classes referenced from that class.

Is it possible to load a class by two ClassLoader?

A class is always identified using its fully qualified name (package. classname). So when a class is loaded into JVM, you have an entry as (package, classname, classloader). Therefore the same class can be loaded twice by two different ClassLoader instances.

Which entity has a reference to the ClassLoader that defined it?

Every Class object contains a reference to the ClassLoader that defined it.


1 Answers

The short answer is that the implementation is part of Sun's internal workings and not available through public means. getURLs() will only ever return the URLs that are passed in. There is a longer answer but it is only for the daring.

Stepping through Oracle JVM 8 with the debugger has led me through pretty much an identical structure as OpenJDK6 and you can see where it loads the class path here.

Basically, the class loader keeps a stack of URLs it has not yet parsed into memory. When asked to load a class it will pop URLs off the stack, load them as class files or jar files, and if they are jar files it will read the manifest and push class path entries onto the stack. Each time it processes a file it adds the "loader" which loaded that file to a loader map (if nothing else, to ensure it doesn't process the same file multiple times).

You can access this map if you are really motivated to do (would not recommend it) with:

        Field secretField = URLClassLoader.class.getDeclaredField("ucp");
        secretField.setAccessible(true);
        Object ucp = secretField.get(loader);
        secretField = ucp.getClass().getDeclaredField("lmap");
        secretField.setAccessible(true);
        return secretField.get(ucp);

Running that on a dummy setup where I have dummy-plugin.jar which references external.jar (in the manifest of dummy-plugin.jar) I get the following:

1) Immediately after creating the class loader (before loading any class):

urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
getSecretUrlsStack=[file:.../dummy-plugin.jar]
getSecretLmapField={}

2) After loading a class from dummy-plugin.jar:

urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
getSecretUrlsStack=[file:.../external.jar]
getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb}

3) After loading a class from external.jar:

urlClassLoader.getURLs()=[file:.../dummy-plugin.jar]
getSecretUrlsStack=[]
getSecretLmapField={file:.../dummy-plugin.jar=sun.misc.URLClassPath$JarLoader@736e9adb, file:.../external.jar=sun.misc.URLClassPath$JarLoader@2d8e6db6}

Oddly enough this seems to fly in the face of the JDK for URLClassLoader:

The classes that are loaded are by default granted permission only to access the URLs specified when the URLClassLoader was created.

like image 162
Pace Avatar answered Oct 19 '22 01:10

Pace