8.1 How Classes Are Loaded

Class loading takes place in two phases. In the first phase, called loading, the name of a class is used to find some chunk of bytes in the form of a class file (as described in chapter 9), and those bytes are introduced to the JVM as the implementation of the class. The ClassLoader also loads the superclass (which involves loading the superclass of the superclass, and so on). After loading, the virtual machine knows the name of the class, where it fits into the class hierarchy, and what fields and methods it has.

In the second phase, called linking or resolution, the class is verified to ensure that it is well formed and doesn't try to violate any of the virtual machine's security constraints. Then the static initializer <clinit> is invoked. Other classes may be loaded as a side effect of the verification process. After linking, the class is ready to use.

This two-stage process allows classes to reference one another without causing infinite loops when two classes reference each other. If class Student has a field of class Teacher, and class Teacher has a field of class Student, then you can load Student without loading Teacher, and vice versa. Whichever one you need first is linked, and all other classes it uses are loaded but not linked until you actually require them. This also helps improve performance by delaying class loading and linking until it's absolutely necessary. The linking procedure may take some time, and if it can be avoided in some cases, then a program will run that much faster.

8.1.1 Loading

A class loader is a subclass of the class java.lang.ClassLoader. The load phase starts in a method called loadClass. ClassLoader itself has a method called loadClass, but it's abstract, so it has no implementation. Subclasses of ClassLoader must provide an implementation. The descriptor of loadClass is

 Class loadClass(String name, boolean resolve); 

where name represents the name of the class that loadClass is to load. The name will be fully qualified (that is, it might be java.lang.Object, not Object), and it will contain periods (.), not slashes (/), to separate package components. The parameter resolve tells loadClass whether or not to proceed to the linking stage.

The class loader uses name to find a bunch of bytes that it wants to load as a class. These bytes can be anywhere: on disk, in an array in memory, in a database, over the network. They can be encrypted or in a nonstandard class file format. It is the responsibility of loadClass to convert them into a form that the virtual machine can understand.

Next the class loader has to inform the virtual machine that this particular set of bytes forms the class it's looking for. This is done with the defineClass method in ClassLoader. Since defineClass is protected, only subclasses of ClassLoader can introduce new classes into the system. Combined with the checkCreateClassLoader method of SecurityManager, which can be used to prevent new ClassLoaders from being created, this gives the system a degree of control over who can load new classes into the system.

The method defineClass takes the array of bytes and makes some superficial checks that the class file is properly formatted. Then defineClass calls loadClass to load (but not link) the superclass. The method loadClass loads the superclass, using defineClass to define it, and so on. Eventually it finds either java.lang.Object, which has no superclass, or defineClass finds that it has gone in a loop. If defineClass detects a loop, it throws a ClassCircularityError. This prevents the class loader from succeeding, so the circular class definition is never loaded.

The method defineClass returns a Class object. At this point, the class is loaded, but it is not ready for use. The virtual machine knows the name of the class, the names of its superclasses, and the names and types of its fields and methods. It has not necessarily loaded any of the interface classes yet, because they don't contribute to the set of methods that the class supports. They only contribute to the methods it's supposed to support, and this is checked during linking.

While loading superclasses, the process may eventually come to a class that the class loader can't find but is instead expected to come from the system itself. For example:

 class CoolWidget extends java.awt.Panel 

The class loader loading CoolWidget probably does not find a definition for java.awt.Panel, since that's a standard system class. The class loader can ask the system to load the class by calling findSystemClass, passing it the string "java.awt.Panel". The method findSystemClass returns a Class object for java.awt.Panel. This class has no class loader; if you call getClassLoader on the Class object, the result will be null.

8.1.2 Linking

The class must be linked before it can be used. During linking, the class is verified to make sure it meets certain criteria, and initialization takes place. Linkage happens in a method in ClassLoader called resolveClass. The method resolveClass is usually called at the end of loadClass when loadClass is asked to resolve the class (that is, when the resolve argument is true). You don't have to write resolveClass; it's defined in ClassLoader and marked final. Before a class can be linked, its superclass must be linked, and so on.

The first thing resolveClass does is verification. Verification is such an important topic that this book devotes a whole chapter (chapter 6) to the subject. During verification, the JVM ensures that classes obey certain rules, among them

  • All the methods required by the interfaces are implemented.

  • The instructions use constant pool references correctly (that is, the constant actually exists, and it's of the correct type).

  • The methods don't overflow the stack.

  • The methods don't try to use an int as a reference.

Once the class is successfully verified, the class is initialized. Space is allocated for all of the static fields and the JVM assigns default values (either the standard default values of 0 for numbers and null for references or some other value if there is a ConstantValue attribute attached to the field).

Finally, the virtual machine invokes the <clinit> method of the class. In a Java program, <clinit> is where the Java compiler places all the code that appears outside any method. This includes field initializers and code marked static without a method name. When <clinit> terminates, the class is ready. (If <clinit> threw an uncaught exception, then the class is actually not ready; resolveClass throws an ExceptionInInitializerException, and the Class object can't be used.)



Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net