Dynamic class loading is
an important feature of the Java Virtual Machine because it
provides the Java platform with the ability to install software
components at run-time. It has a number of unique characteristics.
First of all, lazy loading means that classes are loaded on demand
and at the last moment possible. Second, dynamic class loading
maintains the type safety of the Java Virtual Machine by adding
link-time checks, which replace certain run-time checks and are
performed only once. Moreover, programmers can define their own
class loaders that, for example, specify the remote location from
which certain classes are loaded, or assign appropriate security
attributes to them. Finally, class loaders can be used to provide
separate name spaces for various software components. For example,
a browser can load applets from different web pages using separate
class loaders, thus maintaining a degree of isolation between those
applet classes. In fact, these applets can contain classes of the
same name -- these classes are treated as distinct types by the
Java Virtual Machine.
The class loading
mechanism is not only central to the dynamic nature of the Java
programming language. It also plays a critical role in providing
security because the class loader is responsible for locating and
fetching the class file, consulting the security policy, and
defining the class object with the appropriate permissions.
5.1 Class
Loader Class Hierarchies
When loading a class,
because there can be multiple instances of class loader objects in
one Java Virtual Machine, an important question is how do we
determine which class loader to use. The Java 2 SDK has introduced
multiple class loader classes are introduced that have distinct
properties, so another important question is what type of class
loader we should use.
The root of the class
loader class hierarchy is an abstract class called
java.lang.ClassLoader, originally defined in JDK 1.0 and since
expanded. Class java.security.SecureClassLoader, introduced in Java
2 SDK, v 1.2, is a subclass and a concrete implementation of the
abstract ClassLoader class. Class java.net.URLClassLoader is a
subclass of SecureClassLoader.
A utility program
called Appletviewer uses a private class
sun.applet.AppletClassLoader to load applets. In JDK 1.0,
AppletClassLoader is a subclass and concrete implementation of
ClassLoader. In Java 2 SDK, it is a subclass of URLClassLoader.
When creating a custom
class loader class, one can subclass from any of the above class
loader classes, depending on the particular needs of the custom
class loader. Because AppletClassLoader is a private class defined
in the sun.* package, it is not supported and is subject to change,
so one should not subclass from it.
5.2 The
Primordial Class Loader
Because each class is
loaded by its class loader, and each class loader itself is a class
and must be loaded by another class loader, we seem to have the
obvious chicken-and-egg problem, i.e., where does the first class
loader come from? There is a "primordial'' class loader
that bootstraps the class loading process. The primordial class
loader is generally written in a native language, such as C, and
does not manifest itself within the Java context. The primordial
class loader often loads classes from the local file system in a
platform-dependent manner.
Some classes, such as
those defined in the java.* package, are essential for the correct
functioning of the Java Virtual Machine and runtime system. They
are often referred to as base classes. Due to historical reasons,
all such classes have a class loader that is a null. This null
class loader is perhaps the only sign of the existence of a
primordial class loader. In fact, it is easier to simply view the
null class loader as the primordial class loader.
Given all classes in
one Java application environment, we can easily form a class
loading tree to reflect the class loading relationship. Each class
that is not a class loader is a leaf node. Each class's parent
node is its class loader, with the null class loader being the root
class. Such a structure is a tree because there cannot be cycles --
a class loader cannot have loaded its own ancestor class
loader.
5.3 Class
Loader Delegation
When one class loader is
asked to load a class, this class loader either loads the class
itself or it can ask another class loader to do so. In other words,
the first class loader can delegate to the second class loader. The
delegation relationship is virtual in the sense that it has nothing
to do with which class loader loads which other class loader.
Instead, the delegation relationship is formed when class loader
objects are created, and in the form of a parent-child
relationship. Nevertheless, the system class loader is the
delegation root ancestor of all class loaders. Care must be taken
to ensure that the delegation relationship does not contain cycles.
Otherwise, the delegation process may enter into an infinite
loop.
5.4 Class
Resolution Algorithm
The default
implementation of the Java 2 SDK ClassLoader method for loading a
class searches for classes in the following order:
- Check if the class
has already been loaded.
- If the current class
loader has a specified delegation parent, delegate to the parent to
try to load this class. If there is no parent, delegate to the
primordial class loader.
- Call a customizable
method to find the class elsewhere.
Here, the first step
looks into the class loader's local cache (or its functional
equivalent, such as a global cache) to see if a loaded class
matches the target class. The last step provides a way to customize
the mechanism for looking for classes; thus a custom class loader
can override this method to specify how a class should be looked
up. For example, an applet class loader can override this method to
go back to the applet host and try to locate the class file and
load it over the network.
If at any step a class
is located, it is returned. If the class is not found using the
above steps, a ClassNotFound exception is thrown.
Observe that it is
critical for type safety that the same class not be loaded more
than once by the same class loader. If the class is not among those
already loaded, the current class loader attempts to delegate the
task to the parent class loader. This can occur recursively. This
ensures that the appropriate class loader is used. For example,
when locating a system class, the delegation process continues
until the system class loader is reached.
We have seen the
delegation algorithm earlier. But, given the name of any class,
which class loader do we start with in trying to load the class?
The rules for determining the class loader are the following:
- When loading the
first class of an application, a new instance of the URLClassLoader
is used.
- When loading the
first class of an applet, a new instance of the AppletClassLoader
is used.
- When
java.lang.Class.ForName is directly called, the primordial class
loader is used.
- If the request to
load a class is triggered by a reference to it from an existing
class, the class loader for the existing class is asked to load the
class.
Note that rules about the
use of URLClassLoader and AppletClassLoader instances have
exceptions and can vary depending on the particular system
environment. For example, a web browser may choose to reuse an
existing AppletClassLoader to load applet classes from the same web
page.
Due to the power of
class loaders, we severely restrict who can create class loader
instances. On the other hand, it is desirable to provide a
convenient mechanism for applications or applets to specify URL
locations and load classes from them. We provide static methods to
allow any program to create instances of the URLClassLoader class,
although not other types of class loaders.
CONTENTS | PREV | NEXT