DZone: What typically causes a memory leak on web application reload and what symptoms might a developer see?
Mark Thomas: The usual symptom observed is an OutOfMemoryError (OOME) indicating that the Permanent Generation (PermGen) is exhausted when reloading a web application.
PermGen is where the JVM stores classes. Classes are stored in PermGen using class name and class loader as the unique identifier. Each web application has its own class loader; this allows different versions of the same class (with the same name) to be used in different web applications without conflict. A web application also gets a new class loader when it is reloaded. Classes are removed from PermGen when the class loader is collected by the garbage collector. This normally happens after a web application stops. However, if something retains a reference to the web application class loader, it won't be garbage collected and the classes it loaded will remain in PermGen. Since PermGen is limited in size, this usually only has to happen a few times before all the PermGen is used. That is when the OOME occurs.
To prevent the OOME, it is necessary to ensure that nothing retains a reference to the web application class loader.
DZone: Tell me about the Tomcat's history with memory leaks on web application reload.
Mark: Memory leaks on web application reload have been a problem for as long as I have been involved in the Tomcat project. Where the problem has been traced to a bug in the Tomcat code base, it has been fixed. There were a handful of such bugs fixed around the time of Tomcat 4.1.x / Tomcat 5.5.x that regularly caused problems. Since then there have been a few edge case bugs that occasionally caused problems and, again, these were fixed as they were found.
Historically, Tomcat has been blamed for causing these memory leaks but when investigated by the Tomcat developers, it usually emerged that the root cause was a bug in the web application or the library rather than Tomcat.
Around twelve months ago I was giving a presentation on Tomcat tuning and made an off-hand comment that the root cause of memory leaks on web application reload was application and/or library bugs rather than a Tomcat bug. This comment generated rather more feedback than I was expecting and I was soon working with a number of the attendees debugging their memory leaks.
The leaks that were traced to application or library bugs weren't unexpected. However, we also traced some leaks to usage of particular standard Java APIs and this was a surprise. As we worked to develop fixes for these, I realised that these fixes would be best placed as part of the standard Tomcat codebase. That became the starting point for the new memory leak protection.
The memory leak protection was enhanced by taking a number of web applications known to have memory leaks on reload and developing fixes and workarounds for each of the memory leaks found until the applications could be reloaded without triggering a memory leak. The combination of these fixes and workarounds became the new memory leak protection features that will be included in Tomcat 7 and has recently been back-ported to Tomcat 6.0.x (6.0.24 onwards).
DZone: So have all of the bugs causing memory leaks on Tomcat's end been fixed?
Mark: All the ones we know about! There are probably a few rarely encountered bugs remaining. These too, will get fixed as they are reported.
Tomcat's JVM Memory Profile
DZone: What are some of the bugs in web applications, their libraries, and the JVM that have caused memory leaks?
Mark: The memory leaks all fit a common pattern. The web application creates an object of a class loaded by the web application class loader and then places this object in a registry that has been loaded by a different
class loader. This is fine, as long as the object is removed from the registry when the web application stops. If the object is not removed, the registry retains a reference to the object, the object retains a reference to the class and the class retains a reference to the class loader. This pins the class loader in memory. This chain of
references prevents the class loader from being garbage collected which in turn means that the PermGen used to store classes loaded by this class loader cannot be freed.
Application or library code that can trigger this situation include:
- JDBC driver registration
- Some logging frameworks
- Storing objects in ThreadLocals and not removing them
- Starting threads and not stopping them
There are also a number of places that using the standard Java API can trigger a similar issue. These include:
- Using the javax.imageio API (the Google Web Toolkit can trigger this)
- Using java.beans.Introspector.flushCaches() (Tomcat does this to prevent memory leaks caused by this caching)
- Using XML parsing (the root cause is unknown due to a bug in the JRE)
- Using RMI (somewhat ironically, causes a leak related to the garbage collector)
- Reading resources from JAR files
DZone: Tell me about the work-arounds that Tomcat 7 will have for these bugs.
Mark: For the application or library code problems, Tomcat fixes problems where it can by de-registrating the object(s) that were registered by the application or library. Tomcat will also log any action taken so the web application developer can fix the root cause of the problem, rather than relying on Tomcat to fix the leak. This is especially important for threads that are not stopped since Tomcat can not do this safely.
The details of how Tomcat fixes these problems may be found in the clearReferences() method of the WebappClassLoader class.
For the Java API usages that can trigger a memory leak, these are all triggered if the first call to that API is made by a web application. Tomcat, therefore, prevents these leaks by ensuring that the core Tomcat code is the first to call these APIs, thereby making them safe for web applications to call.
The details of this protection can be in the JreLeakPreventionListener class.
The one exception to this is the java.util.logging Implementation (JULI). The logging framework provided by the JRE is not class loader aware and can quickly lead to memory leaks in a Servlet container environment. To address this, JULI replaces the standard LogManager with one that is class loader aware. This allows java.util.logging to be used without triggering a memory leak.
DZone: How much memory leak improvement do you think Tomcat 7 will have overthe current version?
Mark: The new features in Tomcat 7 and 6.0.24 onwards should offer significant improvements. However, it is likely that there are other causes of memory leaks since the investigations so far have only used a small number of web applications for testing. The new features in Tomcat should make it easier to track own these additional sources of memory leaks and, as they are identified, protection against them can be added to Tomcat.
DZone: How are things progressing on Tomcat 7 development and where is the community at right now as far as the roadmap is concerned? Is there a ballpark for it's release date?
Mark: Things are progressing well. The JSP & EL 2.2 implementation is complete and the Servlet 3.0 implementation is close to completion. I had hoped that Tomcat 7 would be ready for a first release by the end of January but it now looks as if it will happen in February.
DZone: Is there anything else you'd like to mention about Tomcat 7?
Mark: There has been lots of clean-up and other tweaks in Tomcat 7 but the one thing that I think is worth mentioning is improved support for embedding Tomcat. Whilst Tomcat has always been embeddable, it has been rather more complex than it needs to be. With Tomcat 7 we will be providing a simple API and a download option that provides all the Tomcat features but with a fewer number of JARs. Together, these changes will make it much easier to embed Tomcat in other applications.
Finally, the Tomcat community is always looking for new contributors. Whether it is answering questions on the users list, providing translations, fixing bugs, developing new features, improving the web site or something else, contributions are always welcome. The first step for people interested in contributing is to join the user and/or dev mailing list and let the Tomcat community know how they can help.