Asked  7 Months ago    Answers:  5   Viewed   24 times

Does anyone know how to programmaticly find out where the java classloader actually loads the class from?

I often work on large projects where the classpath gets very long and manual searching is not really an option. I recently had a problem where the classloader was loading an incorrect version of a class because it was on the classpath in two different places.

So how can I get the classloader to tell me where on disk the actual class file is coming from?

Edit: What about if the classloader actually fails to load the class due to a version mismatch (or something else), is there anyway we could find out what file its trying to read before it reads it?

 Answers

45

Here's an example:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

This printed out:

file:/C:/Users/Jon/Test/foo/Test.class
Tuesday, June 1, 2021
 
inVader
answered 7 Months ago
48
String javaHome = System.getProperty("java.home");

Can you tell me either through pure Java ... on windows how is it possible to find out the location of javaw.exe?

E.G.

import java.io.File;

class JavawLocation {

    public static void main(String[] args) {
        String javaHome = System.getProperty("java.home");
        File f = new File(javaHome);
        f = new File(f, "bin");
        f = new File(f, "javaw.exe");
        System.out.println(f + "    exists: " + f.exists());
    }
}

Output

C:Program Files (x86)Javajdk1.6.0_29jrebinjavaw.exe    exists: true
Press any key to continue . . .

And yes, I am confident that will work in a JRE.

Monday, June 14, 2021
 
Nickool
answered 6 Months ago
60

The problem is indeed that the interface ItemProcessor is not on the classpath. Notice that the error states "find or load main class". In the case of BarNotThere the JVM is really not able to find the main class. But in the Bar case, it is not able to load the main class.

In order to completely load a class, the JVM also need instances of each superclass objects. During this process for Bar, the JVM tries to load the class object for ItemProcessor. But since this interface is not on the classpath, loading of the main class Bar fails and the startup terminates with the Error: Could not find or load main class Bar.

If you struggle with finding the problematic class in question (because the is no message saying so), you can use the jdeps tool to inspect the classpath. Just use the same classpath, but run jdeps instead of java:

$ jdeps -cp foobar.jar Bar
foobar.jar -> java.base
foobar.jar -> not found
   <unnamed> (foobar.jar)
      -> java.io
      -> java.lang
      -> javax.batch.api.chunk                              not found

(This was created using openjdk-9, actual output may vary heavily depending on the Java version)

This should give you enough hints as where to look for the missing class.


Further explanation

Notice the difference between loading and initializing a class. If classloading fails during initialization (which means the class was successfully found and loaded), you will get your expected ClassNotFoundException. See the following example:

import javax.batch.api.chunk.ItemProcessor;

public class FooBar {

    private static ItemProcessor i = new ItemProcessor() {
        @Override
        public Object processItem(Object item) throws Exception {
            return item;
        }
    };

    public static void main(String[] args) {
        System.out.println("Foo");
    }
}

In this case, the class FooBar can be loaded during startup. But it can not be initialized, since the static field i needs the ItemProcessor class, which is not on the classpath. Initialization is a precondition if a static method on a class is executed, which is the case, when the JVM tries to invoke the main method.

$ java -cp foobar.jar FooBar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: javax/batch/api/chunk/ItemProcessor
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
        at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
        at java.lang.Class.getMethod0(Class.java:3018)
        at java.lang.Class.getMethod(Class.java:1784)
        at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: javax.batch.api.chunk.ItemProcessor
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 7 more
$
Friday, July 30, 2021
 
Wickethewok
answered 4 Months ago
99

I think it can happen if

  1. a SomeClass instance was loaded from ClassLoader X (so its class is SomeClass of CL X or let's call it: CL(X).SomeClass)
  2. but it is being cast in a different class loader. E.g. the current Threads class loader is Y so SomeClass is actually CL(Y).SomeClass

So you have:

  • instance class = CL(X).SomeClass
  • class cast target = CL(Y).SomeClass

Or in other words - not the same class - thus the class cast exception.


Possible duplicate of: ClassCastException when casting to the same class - it has some good suggestions as well.

Wednesday, August 18, 2021
 
antoniputra
answered 4 Months ago
13

The term “System class loader” is a misnomer. As you stated correctly, it’s responsible for loading the classes from locations of the class path, which are the application classes.

As of Java 8, both, AppClassLoader and ExtClassLoader, are subclasses of java.net.URLClassLoader, which is a subclass of java.security.SecureClassLoader, which is a subclass of java.lang.ClassLoader. All of these classes are loaded by the Bootstrap loader, solving the chicken-and-egg problem.

Each runtime class has a defining class loader. For those classes defined by the Bootstrap loader during startup, the defining class loader is the Bootstrap loader. When the JVM initialization is completed and an attempt to start an application is made, the Application class loader (aka System class loader) will be queried for the main class. The Application class loader will follow the standard delegation model querying the parent first, likewise does the Extension class loader and whichever class loader will create the class will be the class’ defining class loader.

Now, when resolving a class referenced by another class or when Class.forName(String) is invoked, the defining loader of the class containing the reference will be used to resolve the class. So when the Application class loader has loaded your class myapp.foo.Bar and it contains a reference to javax.swing.JButton, its defining class loader, i.e. the Application class loader, will be queried for that class, follow the delegation model to end up with a javax.swing.JButton defined by the Bootstrap loader. So class references within javax.swing.JButton are resolved only through the Bootstrap loader, which implies that javax.swing.JButton can not contain a reference to your myapp.foo.Bar class, as it is not in scope.

So, the JVM does not always starts with “System class loader” for loading a class, but only for resolving class references of classes defined by it (or a child loader) or when being queried explicitly, like when resolving the main class.

There are 3rd party class loaders not strictly following the parent delegation model, but regardless of how and to which loader they delegate, there will be a defining loader for each class (the one returned by getClassLoader()), which will be the one used for resolving references within the class. The JVM ensures that identical symbolic names within one class always resolve to the same runtime class, regardless of how the particular class loader implements lookups.

Note that in Java 9, the Extension class loader has been replaced by the Platform class loader. This class loader may deviate from the simple parent delegation, i.e. it might delegate to the Application class loader for loading application provided modules that supersede a platform provided module. Also, the builtin class loaders are not subclasses of URLClassLoader anymore.

Tuesday, November 9, 2021
 
Gil
answered 3 Weeks ago
Gil
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share