You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2005/11/12 18:49:00 UTC

svn commit: r332801 - /tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java

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: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r332801 - /tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java

Posted by Peter Rossbach <pr...@objektpark.de>.
Hey Remy,

thanks for work at this issue, currently I have comment out the static 
field handling for my tests.

Wait and see ;-)
Peter


Remy Maucherat schrieb:

> Peter Rossbach wrote:
>
>> Hey Remy,
>>
>> I have problems with your last ClassLoader changed.
>>
>> When I used following code:
>>
>>    protected static final String info = 
>> "org.objektpark.catalina.session.LogSessionListener/1.1";
>
>
> Yes, I know, I have more changes (which may include commenting out the 
> thing). Sorry for the trouble.
>
> Unfortunately, I am having problems getting testing done :(
>
> Rémy
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>
>
>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r332801 - /tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java

Posted by Remy Maucherat <re...@apache.org>.
Peter Rossbach wrote:
> Hey Remy,
> 
> I have problems with your last ClassLoader changed.
> 
> When I used following code:
> 
>    protected static final String info = 
> "org.objektpark.catalina.session.LogSessionListener/1.1";

Yes, I know, I have more changes (which may include commenting out the 
thing). Sorry for the trouble.

Unfortunately, I am having problems getting testing done :(

Rémy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r332801 - /tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java

Posted by Peter Rossbach <pr...@objektpark.de>.
Hey Remy,

I have problems with your last ClassLoader changed.

When I used following code:

    protected static final String info = 
"org.objektpark.catalina.session.LogSessionListener/1.1";

I got following StackTrace when I shutdown the server:
INFO   | jvm 1    | 2005/11/17 11:57:08 | 17.11.2005 11:57:08 
org.apache.catalin
a.loader.WebappClassLoader clearReferences
INFO   | jvm 1    | 2005/11/17 11:57:08 | INFO: Could not set field info 
to null
 in class org.objektpark.catalina.session.LogSessionListener
INFO   | jvm 1    | 2005/11/17 11:57:08 | 
java.lang.IllegalAccessException: Fiel
d is final
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.UnsafeQualifiedSt
aticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.jav
a:59)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
java.lang.reflect.Field.set(F
ield.java:656)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.loader.We
bappClassLoader.clearReferences(WebappClassLoader.java:1587)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.loader.We
bappClassLoader.stop(WebappClassLoader.java:1480)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.loader.We
bappLoader.stop(WebappLoader.java:706)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Stan
dardContext.stop(StandardContext.java:4325)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Cont
ainerBase.removeChild(ContainerBase.java:892)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.H
ostConfig.undeployApps(HostConfig.java:1159)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.H
ostConfig.stop(HostConfig.java:1131)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.H
ostConfig.lifecycleEvent(HostConfig.java:312)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.util.Life
cycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Cont
ainerBase.stop(ContainerBase.java:1053)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Cont
ainerBase.stop(ContainerBase.java:1065)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Stan
dardEngine.stop(StandardEngine.java:447)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Stan
dardService.stop(StandardService.java:512)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.core.Stan
dardServer.stop(StandardServer.java:734)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.C
atalina.stop(Catalina.java:601)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.NativeMethodAcces
sorImpl.invoke0(Native Method)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.NativeMethodAcces
sorImpl.invoke(NativeMethodAccessorImpl.java:39)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.DelegatingMethodA
ccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
java.lang.reflect.Method.invo
ke(Method.java:585)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.B
ootstrap.stop(Bootstrap.java:287)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.apache.catalina.startup.B
ootstrap.main(Bootstrap.java:409)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.NativeMethodAcces
sorImpl.invoke0(Native Method)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.NativeMethodAcces
sorImpl.invoke(NativeMethodAccessorImpl.java:39)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
sun.reflect.DelegatingMethodA
ccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
java.lang.reflect.Method.invo
ke(Method.java:585)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.tanukisoftware.wrapper.Wr
apperStartStopApp.stop(WrapperStartStopApp.java:282)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.tanukisoftware.wrapper.Wr
apperManager.stopInner(WrapperManager.java:1982)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.tanukisoftware.wrapper.Wr
apperManager.handleSocket(WrapperManager.java:2391)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
org.tanukisoftware.wrapper.Wr
apperManager.run(WrapperManager.java:2696)
INFO   | jvm 1    | 2005/11/17 11:57:08 |       at 
java.lang.Thread.run(Thread.j
ava:595)
INFO   | jvm 1    | 2005/11/17 11:57:08 | 17.11.2005 11:57:08 
org.apache.catalin
a.cluster.session.DeltaManager stop



remm@apache.org schrieb:

>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: dev-unsubscribe@tomcat.apache.org
>For additional commands, e-mail: dev-help@tomcat.apache.org
>
>
>
>
>  
>





---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org