Each time a class is instantiated as an object or referenced statically, that class must be loaded by the Java Virtual Machine (JVM) into memory, Tomcat uses class loaders and has different behavior to other class loaders. Tomcat allocates a class loader to each web application, but there are global ones as well.
Class loader Overview
The class loader deals with loading classes that many be on a file system, embedded computer systems (ROM chip), across a network. The role of a class loader is to abstract the process of loading classes, making completely independent of any type of underlying data store.
Since J2SE 1.2 the JVM uses three different class loaders
- Bootstrap class loader (also called the primordal class loader)
- Extension class loader
- System class loader
These three class loaders are in a hierarchy with the system class loader at the bottom and the bootstrap class loader at the top.
The bootstrap class loader is used by the JVM to load classes that are necessary for the JVM to function, it loads all the core classes (java.lang.*, java.io.*, etc). The bootstrap class loader is included in the JVM to solve the chicken and egg problem, it uses native code to do this. The location to load the core Java classes varies from vendor to vendor, you can use the option -Xbootclasspath/p: and -Xbootclasspath/a: which allows you to append to the classpath.
The extension class loader is an alternative class loader that is used instead of the classpath environment, you can drop JAR files into this directory and the JVM will pick them up, again vendors may use different directory locations. This area is normally used when the JAR files are not part of the standard core files but need to be part available to all Java applications, for an example you could put Java’s cryptography extension in this directory. Be careful as dropping the wrong JAR files into this directory can cause a security as any Java application will have access this these classes.
The system class loader locates its class files specified in the classpath environment variable, it is also used to load an application’s entry point class (that is, the class with the main() method) and is the default class loader for loading in any other classes not covered by the other two class loaders. The system class loader is sometimes called the application class loader, this is more appropriate as it is the class loader for loading the application classes.
The Delegation Model
The JVM knows which class to use by using the delegation model, when a class is requested it first asks its parent to fulfil the request, in other words it delegates the request to its parent class loader, this continues until the bootstrap class loader is reached. Only if a class loaders parent fails to load the class does the original class loader attempt to load the class, basically it goes up to the bootstrap class loader then back down again until the class is found, see the diagram below
If one of the parent class loader finds the class, it is passes it down the stack as a class object which is passed to the JVM, otherwise a ClassNotFoundException is thrown.
Endorsed Standard Override Mechanism
As of Java 1.4 there is a concept called “Endorsed Standard Override Mechanism”. Say you have a newer version of the JAXP class, if you place it into the extension class loader directory it will not get used because the bootstrap class loader will use the older version in the rt.jar, to overcome this problem there is a directory $JAVA_HOME/lib/endorsed which you place JAR files that the bootstrap class loader will load class files instead, thus it will use the newer version. You can also use the -Djava.endorsed.dir=/path/to/lib/endorsed parameter to point to the endorsed directory. One point to make is that only certain packages can be overridden, at this point only CORBA and JAXP classes can be overridden but you may want to check Sun Java’s web site for the most current packages that you can override.
There are three aspects of a class loader behavior
- Lazy Loading
- Class Caching
- Separate Namespaces
None of the class loaders preloads classes, they load on-demand. This behavior is said to be called “lazy loading” because the object waits to load the data until it is requested. This can be good the following reasons
- Faster performance: At the time of initialization, if each class loader had to load every class, it would take much longer to initialize the JVM
- Efficiency: Loading all classes would require lots more memory
- Flexibility: JAR files and classes can be added to the search path of all the class loaders after the class loaders have been initialized.
Class Caching means that when a class is loaded into a class loader it will stay loaded for a period of time, however the garbage collector can reclaim these class objects providing they are not being used, you can turn this off by using the parameter -Xnoclassgc.
Separate Namespaces is that each class loader is assigned a unique namespace, thus if both the bootstrap and the system class loader load calls uk.co.data.PackageA, they are considered to be two different packages, they do not have access to each others private/protected members.
Security and Class Loaders
The Java class loader architecture uses the following
- Class loader delegation
- Core class restriction
- Separate class loader namespaces
- A security manager
The class loader delegation is described as a security feature as the bootstrap class loader has first shot at any class and it will always find the real copies of the core Java classes. However because you can write your own class loader it can be compromised.
The core class restriction helps with security because the class loader abstract class blocks the creation of any class whose fully qualified names begins with java. Thus no false java.* classes can be caught hanging around and because the bootstrap loader is not written in Java and does not descend from ClassLoader it is not itself subject to restriction. Thus the delegation model by itself does not provide security, instead the core class restriction mechanism prevents rogue class loaders from tampering with the core Java libraries (at runtime).
Having separate class loader namespaces means that it stops web application stepping over each other when class loading classes, further more classes loaded by different class loaders but otherwise in the same package cannot access each others package-private members.
You can use the SecurityManager to have total control over an web application including class loading security
- Prevent the loading of any class loader
- Prevent a reference to any class loader being obtained (including the system class)
- Prevent the context class loader of any thread being changed
I will discuss more on how the SecurityManager works in a later topic.
Tomcats Class Loaders
Below is a diagram of Tomcats Class Loaders, as you can see it is different to the Java version
Tomcat uses the default system class loader, but does something different, in the startup file Tomcat clears the classpath environment variable and points Tomcat to two Tomcat files bootstrap.jar (startup classes) and tomcat-juli.jar (logging API) .
The endorsed directory is located $CATALINA_HOME/endorsed, in this directory you have the popular Apache Xerces XML parser and a version of the JAXP API, however in the version I downloaded this directory did not exists.
The common class loader is responsible for classes that are used by Tomcat and publicly available to all Web applications, it is located in $CATALINA_HOME/lib, there are a number of JAR files in this directory. Be aware that if you want you Web application to be portable then you should not put its JAR files here but keep them within the application, unless all Web application use them.
The Web application class loader is were you put your own application specific JARs and class files, this is located in webapps/<webapp>/WEB-INF/classes and webapps/<webapp>/WEB-INF/lib. This class loader is different as it does not use delegation, instead it tries to load classes first, before delegating the request to the other class loaders, there is an exception to this, you cannot override the Java core libraries (java.*, javax.*, etc). Also each Web application has its own instance of this class loader, which means that no two Web applications can see each others class files.
Here is a class order table to recap on the locations (its in order)
|Web application class loader||$CATALINA_HOME/webapps/<webapp>/WEB-INF/lib
|bootstrap class loader||Looks in the core Java classes $JAVA_HOME/jre/lib, it will also look in $CATALINA_HOME/endorsed|
|Extension class loader||$JAVA_HOME/jre/lib/ext|
|system class loader||$CATALINA_HOME/bin/bootstrap.jar
|common class loader||$CATALINA_HOME/lib|
Recapping once a class is loaded it is cached in memory, thus if a request for a class the cached version is returned. If you change a class this is ignored because the cached copy is used. However because Tomcat uses its own Web application class loader, it can perform dynamic reloading of classes, it does this by halting the application and then reloading it using the new class. The original cached copy is then orphaned and thus the garbage-collected, this eliminates the need to restart the JVM.
You can use to methods to reload a class dynamically
- Configure Tomcat to scan the Web applications WEB-INF/classes and WEB-INF/lib directories for changes
- Explicitly reload the Web application with the Tomcat Manager application.
One common pitfall regards the use of a singleton class, this must be located in the common class loader so that only one instance of this class is initialized and can be accessed by all Web application. Sometimes when a singleton is not referenced but you still want the contents it will get garbage collected, to stop this you can use the Java option -Xnoclassgc, be careful as this means no class garbage collection will take place and could cause the application to use all memory, a better way would be to make use the object is always referenced.