31

Is there a way to determine which classes are loaded from which JARs at runtime?

I'm sure we've all been in JAR hell before. I've run across this problem a lot troubleshooting ClassNotFoundExceptions and NoClassDefFoundErrors on projects. I'd like to avoid finding all instances of a class in JARs and using process of elimination on the code causing a CNFE to find the culprit.

Will any profiling or management tools give you this kind of information?

This problem is super annoying purely because we should have this information at the time the class gets loaded. There has to be a way to get to it, or record it and find it, yet I know of nothing that will do this, do you?

I know OSGi and versioned bundles/modules aim to make this a non issue... but it doesn't seem to be going away any time soon.

Note: I found this question is a subset of my question related to classes loaded from versioned jars.

Somewhat related, this post explains a strategy to search for a class within JARs (either under the current directory) or in your M2_REPO: JarScan, scan all JAR files in all subfolders for specific class

Also somewhat related, JBoss Tattletale

Null
  • 1,950
  • 9
  • 30
  • 33
cwash
  • 4,185
  • 5
  • 43
  • 53
  • Jason Day has it right, this basically a duplicate of a question I asked not so long ago. http://stackoverflow.com/questions/779650/where-on-the-file-system-was-my-java-class-loaded-from – shsteimer Jun 03 '09 at 21:35

5 Answers5

59

Passing the -verbose:class switch to the java command will print each class loaded and where it was loaded from.

Joops is also a nice tool for finding missing classes ahead of time.

Jason Day
  • 8,809
  • 1
  • 41
  • 46
  • Nice - Joops looks cool - do you know if it is smart enough to follow a class referenced through Class.forName? – cwash Jun 03 '09 at 22:08
  • I would be surprised if it could follow Class.forName references, but I don't know for sure. Joops also has a `which` command, so you could use that to manually check for classes by name. – Jason Day Jun 04 '09 at 12:19
  • +1 Thanks a ton! Helped me figure out conflicting class loading! – shrini1000 Feb 15 '13 at 06:50
  • 1
    How about already running JVM which I don't have control over? i.e. I can't restart it with -verbose:class option. may not be possible since jvm never keeps log of it without -verbose:class option but just wondering. – nir Feb 09 '17 at 04:03
15

From code you can call:

myObject.getClass().getProtectionDomain().getCodeSource()

(Note, getProtectionDomain may unfortunately return null (bad design), so "proper code" would check for that.)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • This wouldn't help with a CNFE or NCDFE; when have you done this? – cwash Jun 03 '09 at 21:13
  • Presumably if you get a ClassNotFoundException then you presumably already know the class is not in any of the jars. (Assuming you are not playing with class loaders.) There may be issues if there are other errors during class loading, like a missing superclass. – Tom Hawtin - tackline Jun 03 '09 at 23:25
  • 1
    @TomHawtin-tackline you can get `ClassNotFoundException` if a static initializer of the said class throws an exception. Then you have the class in the jar file, but the ClassLoader can not load it. – hidralisk Nov 14 '13 at 16:54
  • This can be used in an Eclipse debugging Expression if you don't want to (or can't) modify the code. – jwaddell Feb 06 '14 at 22:33
4

There is an MBean for the JVM flag mentioned by Jason Day above.

If you are using JBoss, you can twiddle this on demand using JMX, if you add the native JMX MBean server to your config. Add the following -D's:

-Dcom.sun.management.jmxremote.port=3333
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djboss.platform.mbeanserver 
-Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl
-DJBOSS_CLASSPATH="../lib/jboss-system-jmx.jar"

And then you can see this setting under the java.lang:Classloading MBean and can cut it on/off on the fly. This is helpful if you only want it on while executing a certain piece of code.

There is also an MBean which will allow you to enter a fully qualified classname and see where it was loaded from in the class hierarchy. The MBean is called LoaderRepository and you'll want to invoke the displayClassInfo() operation, passing in the FQCN.

cwash
  • 4,185
  • 5
  • 43
  • 53
0

In WebSphere (WAS) you can use a feature called "Class Loader Viewer"

Enable the class loader viewer first by clicking Servers > Server Types > WebSphere application servers > server_name > Class loader viewer service, enable the service and restart the server.

Then you can go to Troubleshooting > Class Loader Viewer and searching for your class or package name.

https://www-01.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/ttrb_classload_viewer.html?lang=en

cwash
  • 4,185
  • 5
  • 43
  • 53
0

You can easily export a JMX operation to access package info for any loaded class in you process like:

  public static final class Jmx {

    @JmxExport
    public static Reflections.PackageInfo getPackageInfo(@JmxExport("className") final String className) {
      return Reflections.getPackageInfo(className);
    }
  }

and here is a simple unit test to export and invoke it:

  @Test
  public void testClassLocator() throws IOException, InstanceNotFoundException, MBeanException, ReflectionException {
    Registry.export(Jmx.class);
    Reflections.PackageInfo info = (Reflections.PackageInfo) Client.callOperation(
            "service:jmx:rmi:///jndi/rmi://:9999/jmxrmi",
            Jmx.class.getPackage().getName(),
            Jmx.class.getSimpleName(), "getPackageInfo", Registry.class.getName());
    System.out.println(info);
    Assert.assertNotNull(info);
  }

this is all based using some small utilities library from spf4j (http://www.spf4j.org)

you can see this code at and the test at

user2179737
  • 493
  • 3
  • 6