Do I really need all those jars in my classpath?
Big applications have a tendency to accumulate enormous
classpaths. Looking at such a classpath, you might be hard put
to know whether any given jar is really needed. Perhaps it was
needed at the time it was added, but that need has long since
evaporated. How can you tell? Having jars you don't need means
your application will be slower starting up, and perhaps also
while running. It also means that you might be worrying
unnecessarily about getting the latest version of a jar that
you're not actually using.
Alyoshin has an elegant solution using a href="http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html">java.lang.instrument
agent. The basic idea is that you run your application with
java -javaagent:loosejar.jar ... and the
loosejar.jar can find all the classes that
the application has loaded using href="http://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html#getAllLoadedClasses()">Instrumentation.getAllLoadedClasses().
Then for each of those classes it can find what jar it came
from. Thus for each jar on the classpath it can compute the
number of classes that have actually been loaded from it. When
this number is zero, the jar might be unnecessary.
Of course for the results to be valid you have to exercise the
application so that it does everything it can do. That might
not always be easy, but for a candidate jar you can often figure
out what to do to make the jar be referenced. To help you do
this incrementally, the loosejar agent exports a href="http://java.sun.com/jmx" rel="tag">JMX MBean that
allows you to ask for a report while the application is
running. So you can connect with JConsole and get a report, see
what jars might be unnecessary, try to provoke class-loading
from those jars, get another report, and so on.
One subtlety is that the set of jars is not just the contents
of the classpath. The jar could reference other jars through a href="http://java.sun.com/docs/books/tutorial/deployment/jar/downman.html">Class-Path
entry in its manifest. You'd like to know if all of those other
jars are really necessary too. Kyrill and I were unable to find
a better way to get the href="http://en.wikipedia.org/wiki/Transitive_closure">transitive
closure of referenced jar files than to use href="http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html#getResources(java.lang.String)">ClassLoader.getResources("META-INF/MANIFEST.MF")
and parse the returned
jar:URLs. Every jar has a
META-INF/MANIFEST.MFfile, so every jar known by
the ClassLoader will show up in the result of this call, but
ugh. There has to be a better way.