Memory Leaks

There is an excellent discussion about memory leaks given by Frank Kieviet on his blog. In summary, web applications are loaded with their own class loader, so that the web application and all the classes it used can be removed from memory when the web application is reloaded. If this were not the case, then the only way to reload one web application would be to restart the servlet engine, forcing all web applications to restart.

This is an excellent design. However, it is possible that the programmer can cause this design to fail. If there is a reference from a different class loader to the web application class loader, then none of the class data will be released when the web application is reloaded. Such a situation is known as a memory leak.

There are two types of memory leaks: catastrophic and inefficient. Catastrophic errors can cause the servlet engine to crash. Inefficient errors will usually not cause the servlet engine to crash, they will only force the engine to hold onto more resources than it needs.

The catastrophic leaks are the ones that Frank discusses in his blog. If a web application that has a catastrophic leak is reloaded enough times, then it will crash the servlet engine. Each time the servlet engine reloads a web application, a new class loader is created for all the classes that the web application needs to create. The problem with a leak is that the old class loader, which has references to all the classes that were created before the reload, is not released. Each time it is reloaded, all the class data from the old web application class loader cannot be released and then a new class loader is created. All the new classes that are allocated will be allocated from the new class loader. When the web application is reloaded again, there will then be three separate copies of the web application class loader. If this is repeated enough times, all of the memory in the servlet engine will be used and the engine will crash.

The inefficient error will not cause the servlet engine to crash, it will only hold onto more resources than it needs. For example, if a class has a static variable that points to an object, then that will cause the JVM to keep a reference to the class loader that loaded the object. However, once the static variable is assigned, it will not cause another leak, since there is only one static variable per class. Even if a new object is assigned to the static variable, there will not be a catastrophic leak: there will be a pointer to the current class loader, but the pointer to the old class loader will be broken.

If there are several classes that contain static references to objects, then the application could waste a lot of memory. Consider the situation where the application loads one of these classes and is then reloaded. Then the application loads another class with a static object and is then reloaded. There will then be three copies of the web application class loader in memory: one that loaded the first static object, one that loaded the second static object and the current one.

One way to avoid this situation is to use a weak reference for static objects and to use an accessor to retrieve the object. If the object has been garbage collected, then the accessor should reallocate it. This technique would be useful for objects that are not intended to be changed after they have been allocated, like static final objects. Consider the following example, where SomeClass is the name of an arbitrary class.

         private static WeakReference refObject = null;
         public static SomeClass getInstance() {
           SomeClass objInstance = null;
           if (refObject == null) objInstance = new SomeClass();
           refObject = new WeakReference(objInstance);
           objInstance = null;
           return (SomeClass) refObject;

Another aspect of this problem is that static objects are not allocated until they are referenced for the first time. This has implications when one class loader loads a class and another class loader loads the static object in the class. This is very common for loggers. The logger will not be allocated until it is needed, eventhough the class that it is in was loaded earlier. A similar technique can be used for loggers, even if they are not static.

         private static WeakReference refLog = null;
         public static Log getLog() {
           Log log = new LogFactory("some name");
           if (refLog == null) log = new LogFactory("some name");
           refLog = new WeakReference(log);
           log = null;
           return (Log) refLog;

Many of the leaks in the servlet engine could be eliminated if logs were allocated unsing weak references.

Contact the author