ClassLoaders sound intimidating, but if you look
at the source code for one, they are pretty simple. Basically all they do is
find the class file somewhere on disk, read it into RAM, and call java.lang.ClassLoader.defineClass
tell the system to treat ram image as legitimate byte codes. defineClass
and all the other interesting methods are protected, so you will have to write
you own class that extends ClassLoader to get at them.
It will all come clear if you study the source code for ClassLoader
in src.zip.
There are some minor complications dealing with chaining ClassLoaders
so they ask other ClassLoaders first before going to
look on their own.
The default ClassLoader can drive you nuts. Sometimes
it refuses to look on the local hard disk CLASSPATH. It
won’t look in a jar other than the ones mentioned in the <APPLET
ARCHIVE ones. These archives are downloaded en masse whether you need the
files in them or not. The ClassLoader/Security system
won’t let net-loaded classes use pre-installed local DLLs. Unsigned
Applets cannot use custom ClassLoaders.
Custom ClassLoaders
A custom ClassLoader is really quite simple. It need
not even involve writing a new class. You can often just instantiate one of the
existing ClassLoaders. A ClassLoader
needs a private byte[] loadClassData(String name)
method to find the bytes for the class somewhere, and a standard protected
synchronized Class loadClass(String name, boolean resolve) to call your loadClassData.
You inherit everything else you need from ClassLoader.
Why would you ever want a custom ClassLoader?
- They let you change code on the fly without stopping the application. See more
detail on hot code switching below.
- They let you deal with on-the-fly byte code
generated in RAM. If you generate some byte codes in RAM and want to execute
them, you can do it like this without first creating a *.class
file on disk.
- They let you deal with byte code served from a database, local or remote.
- Loading code from a cache. The code may come from a cache, or if it is not there
from a remote server.
- They let you create a sandbox to control exactly what classes are loadable.
- Discourage piracy by dynamically loading frequently-changing small code snippets
over the web while serving the bulk of the app from hard disk.
- They let you deal with byte code coming from an unusual source.
- They let you dynamically change the classpath.
- They help with garbage collection. The static variables in a class and the code
itself will live for for the life of the app if loaded by the default ClassLoader.
If they were loaded by a custom ClassLoader they are
eligible for garbage collection once all references to the custom ClassLoader
and class are gone.
- Say you have inherited some code that uses a singleton to access a database. Now
you want to use more than one database at once. You could go through and replace
all the singleton accesses with a multi-access design. Or you could have two ClassLoader
instances, each one configured for a particular database.
- If you’ve got an intractable memory packratting problem there’s
probably some static variables involved. If you drop the ClassLoader
referencing the static variables, the leaked memory can come back. You can’t
drop the instance of the default ClassLoader, but you
can drop a custom one.
- Let you encrypt your code to obfuscate it.
- Let you store your code in some non-standard form of jar.
Hot Code Changes
ClassLoaders let you modify code on the fly without
shutting down an application. New code is loaded with Class.forname.
It creates new versions of the objects by reloading the classes for the objects
with a ClassLoader. You then have both old and new
versions of the same object, both with the same name, but with different data
and different code. From the JVM’s point of view, a class loaded with a
new ClassLoader is a different animal entirely from
the one loaded with the standard ClassLoader even if
the classes have the same name. You can manipulate both old an new objects with
a common interface.
With a new ClassLoader, you can load a different
version of a class. The old objects continue to use the old code. New objects
use the new code. A given ClassLoader can load a
given class only once. There is no need to unload a class. When the objects
using it are no longer referenced, the class object itself, along with the code,
will be garbage collected. The same classes, loaded by different class loaders
are considered distinct classes. They are not instanceofs
each other!
You will have to instantiate a new ClassLoader every
time you have a new generation of classes. You can load all the replacement
classes of a generation with the same ClassLoader.
However, when you want to replace the replaced classes, you need a yet another
new ClassLoader. You can do this with multiple
instances of the same ClassLoader. You only need to
write one ClassLoader, perhaps not even one, not one
for each generation.
Have a look at the java.net.URLClassLoader. You may
find for your given problem you don’t even have to write whole new ClassLoader,
just instantiate one of Sun’s.
When using Applets or Java Web Start, sometimes Thread.
currentThread(). getContextClassLoader().
getResource() works better than this.
getClass(). getClassLoader().
getResource(). This may bypass a bug in JDK 1.4
and 1.5 now fixed in 1.6+.
Learning More
Sun’s Javadoc on
ClassLoader class : available:
Sun’s Javadoc on
Thread.getContextClassLoader : available:
Sun’s Javadoc on
URLClassLoader class : available:
Sun’s JDK Technote Guide on
How RMI uses ClassLoaders : available:
Sun’s Javadoc on
Class class : available: