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.
Kyrill Alyoshin has an elegant solution using a java.lang.instrument agent. The basic idea is that you run your application with java -javaagent:loosejar.jar ... and the agent in loosejar.jar can find all the classes that the application has loaded using 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 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 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 transitive closure of referenced jar files than to use ClassLoader.getResources("META-INF/MANIFEST.MF") and parse the returned jar: URLs. Every jar has aMETA-INF/MANIFEST.MF file, 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.