Author: remm Date: Sat Nov 12 09:48:54 2005 New Revision: 332801 URL: http://svn.apache.org/viewcvs?rev=332801&view=rev Log: - Add additional experimental measures against apparent garbage collection bugs by setting to null static final fields. Also unregister any JDBC driver. This code is based on techniques found on the Hibernate forums, where this sort of cleanup proved to be able to fix memory leaking. - According to Hibernate developers, the following scenario is causing a leak of the classloader (note: obviously this is not a Tomcat bug, but merely something where there seems to be a workaround):
public class DeployTestServlet extends HttpServlet { private TestValue testValue; public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); testValue = TestHolder.TEST_VALUE; } } public class TestHolder { public static final TestValue TEST_VALUE = new TestValue(); } public class TestValue { private transient ClassLoader value; public TestValue() { value = this.getClass().getClassLoader(); } } Modified: tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java Modified: tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java URL: http://svn.apache.org/viewcvs/tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java?rev=332801&r1=332800&r2=332801&view=diff ============================================================================== --- tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java (original) +++ tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java Sat Nov 12 09:48:54 2005 @@ -23,6 +23,8 @@ import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -33,6 +35,9 @@ import java.security.PermissionCollection; import java.security.Policy; import java.security.PrivilegedAction; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; @@ -1470,20 +1475,9 @@ */ public void stop() throws LifecycleException { - /* - * Clear the IntrospectionUtils cache. - * - * Implementation note: - * Any reference to IntrospectionUtils which may cause the static - * initalizer of that class to be invoked must occur prior to setting - * the started flag to FALSE, because the static initializer of - * IntrospectionUtils makes a call to - * org.apache.commons.logging.LogFactory.getLog(), which ultimately - * calls the loadClass() method of the thread context classloader, - * which is the same as this classloader, whose impl throws a - * ThreadDeath if the started flag has been set to FALSE. - */ - IntrospectionUtils.clear(); + // Clearing references should be done before setting started to + // false, due to possible side effects + clearReferences(); started = false; @@ -1526,11 +1520,6 @@ deleteDir(loaderDir); } - // Clear the classloader reference in common-logging - org.apache.commons.logging.LogFactory.release(this); - // Clear the classloader reference in the VM's bean introspector - java.beans.Introspector.flushCaches(); - } @@ -1561,6 +1550,61 @@ // ------------------------------------------------------ Protected Methods + + /** + * Clear references. + */ + protected void clearReferences() { + + // Unregister any JDBC drivers loaded by this classloader + Enumeration drivers = DriverManager.getDrivers(); + while (drivers.hasMoreElements()) { + Driver driver = (Driver) drivers.nextElement(); + if (driver.getClass().getClassLoader() == this) { + try { + DriverManager.deregisterDriver(driver); + } catch (SQLException e) { + log.warn("SQL driver deregistration failed", e); + } + } + } + + // Null out any static or final fields from loaded classes, + // as a workaround for apparent garbage collection bugs + Iterator loadedClasses = resourceEntries.values().iterator(); + while (loadedClasses.hasNext()) { + ResourceEntry entry = (ResourceEntry) loadedClasses.next(); + if (entry.loadedClass != null) { + Field[] fields = entry.loadedClass.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + int mods = field.getModifiers(); + if (!(!Modifier.isStatic(mods) || !Modifier.isFinal(mods) + || field.getType().isPrimitive() + || field.getName().indexOf("$") != -1)) { + field.setAccessible(true); + try { + field.set(null, null); + } catch (Exception e) { + log.info("Could not set field " + field.getName() + + " to null in class " + entry.loadedClass.getName(), e); + } + } + } + } + } + + // Clear the IntrospectionUtils cache. + IntrospectionUtils.clear(); + + // Clear the classloader reference in common-logging + org.apache.commons.logging.LogFactory.release(this); + + // Clear the classloader reference in the VM's bean introspector + java.beans.Introspector.flushCaches(); + + } + /** * Used to periodically signal to the classloader to release JAR resources. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]