|
|
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
July 25, 2003
Can you find the complete set of loaded classes from within a Java program and then determine from which file each class was
loaded?
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.
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.)
Archived Discussions (Read only)