Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Back to your Class roots, continued

Find runtime origins for <em>all</em> loaded classes

  • Print
  • Feedback

July 25, 2003

QCan you find the complete set of loaded classes from within a Java program and then determine from which file each class was loaded?

A This is another frequently asked Java question. Unfortunately, if you need an adequate solution for "real" product code, the answer is no. If, however, you don't mind something good enough for debugging and testing, then I show you a few tricks below.

From the previous Java Q&A installment, we know that given a Class object, you can usually trace it back to its origins, via the getClassLocation() method explained in that article.

Thus, we just need to find the set of classes currently loaded in the JVM. This, however, is the difficult part: The java.lang.ClassLoader API is not designed to provide any information about classes loaded by a given classloader instance, and Java classloaders are not required to register themselves in any programmatically accessible way. (A Sun Microsystems-compatible JVM will also show classes as they load if you use a -verbose:class JVM option. However, you cannot access this information programmatically.)

Let's tackle this problem in stages. I concentrate on pure Java ideas first and then mention other options later.

All classes loaded by a given classloader

It is not widely known that every java.lang.ClassLoader instance in a Sun-compatible Java 2 Platform, Standard Development Kit (J2SDK) has a private field that is a Vector of classes loaded and defined by that loader instance:

    /*
     * The classes loaded by this classloader. This table's only purpose
     * is to keep the classes from being garbage collected until the loader
     * is garbage collected.
     */
    private Vector classes = new Vector();


Observe that I say "defined" above: this is important. The classloader in question will not necessarily define every class requested via loadClass(): the class can get delegated to the loader's parent instead. Only the classes whose bytes are submitted to java.lang.ClassLoader.defineClass() by a given classloader instance shows up in its classes vector. Also, the JVM directly populates this field, so it always contains valid information (it even contains dynamically created classes such as dynamic proxies).

The following code (available with this article's download) exploits the information in the classes field:

public abstract class ClassScope
{
    public static Class [] getLoadedClasses (final ClassLoader loader)
    {
        if (loader == null) throw new IllegalArgumentException ("null input: loader");
        if (CLASSES_VECTOR_FIELD == null)
            throw new RuntimeException ("ClassScope::getLoadedClasses() cannot" +
            " be used in this JRE", CVF_FAILURE);
        
        try
        {
            final Vector classes = (Vector) CLASSES_VECTOR_FIELD.get (loader);
            if (classes == null) return EMPTY_CLASS_ARRAY;
            
            final Class [] result;
            
            // Note: Vector is synchronized in Java 2, which helps us make
            // the following into a safe critical section:
            
            synchronized (classes)
            {
                result = new Class [classes.size ()];
                classes.toArray (result);
            }
            
            return result;
        }
        // This should not happen if <clinit> was successful:
        catch (IllegalAccessException e)
        {
            e.printStackTrace (System.out);
            
            return EMPTY_CLASS_ARRAY;
        }
    }
    
    
    private static final Field CLASSES_VECTOR_FIELD; // Set in <clinit> [can be null]
    
    private static final Class [] EMPTY_CLASS_ARRAY = new Class [0];
    private static final Throwable CVF_FAILURE; // Set in <clinit>
    
    static
    {
        Throwable failure = null;
        
        Field tempf = null;
        try
        {
            // This can fail if this is not a Sun-compatible JVM
            // or if the security is too tight:
            
            tempf = ClassLoader.class.getDeclaredField ("classes");
            if (tempf.getType () != Vector.class)
                throw new RuntimeException ("not of type java.util.Vector: " +
                tempf.getType ().getName ());
            tempf.setAccessible (true);
        }
        catch (Throwable t)
        {
            failure = t;
        }
        CLASSES_VECTOR_FIELD = tempf;
        CVF_FAILURE = failure;
    }
} // End of class


Note that getLoadedClasses()'s result is a snapshot: the list of loaded classes varies depending on when you call the method. (The list grows over time, however, when the method is invoked on the same classloader instance.)

  • Print
  • Feedback

Archived Discussions (Read only)
Subject
. Forum migration complete By AthenAdministrator
. Forum migration update By AthenAdministrator
. nice By Anonymous
Resources