tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r893564 - in /tomcat/tc6.0.x/trunk: ./ java/org/apache/catalina/core/ java/org/apache/catalina/loader/ webapps/docs/ webapps/docs/config/
Date Wed, 23 Dec 2009 16:31:32 GMT
Author: markt
Date: Wed Dec 23 16:31:31 2009
New Revision: 893564

URL: http://svn.apache.org/viewvc?rev=893564&view=rev
Log:
The remaining memory leak protection changes. ThreadLocals, RMi and an option to stop threads

Modified:
    tomcat/tc6.0.x/trunk/   (props changed)
    tomcat/tc6.0.x/trunk/STATUS.txt
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappLoader.java
    tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
    tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml

Propchange: tomcat/tc6.0.x/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Dec 23 16:31:31 2009
@@ -1,2 +1,2 @@
 /tomcat:883362
-/tomcat/trunk:601180,606992,612607,630314,640888,652744,653247,666232,673796,673820,677910,683969,683982,684001,684081,684234,684269-684270,685177,687503,687645,689402,690781,691392,691805,692748,693378,694992,695053,695311,696780,696782,698012,698227,698236,698613,699427,699634,701355,709294,709811,709816,710063,710066,710125,710205,711126,711600,712461,712467,713953,714002,718360,719119,719124,719602,719626,719628,720046,720069,721040,721286,721708,721886,723404,723738,726052,727303,728032,728768,728947,729057,729567,729569,729571,729681,729809,729815,729934,730250,730590,731651,732859,732863,734734,740675,740684,742677,742697,742714,744160,744238,746321,746384,746425,747834,747863,748344,750258,750291,750921,751286-751287,751289,751295,753039,757335,757774,758249,758365,758596,758616,758664,759074,761601,762868,762929,762936-762937,763166,763183,763193,763228,763262,763298,763302,763325,763599,763611,763654,763681,763706,764985,764997,765662,768335,769979,770716,770809,77
 0876,772872,776921,776924,776935,776945,777464,777466,777576,777625,778379,778523-778524,781528,781779,782145,782791,783316,783696,783724,783756,783762,783766,783863,783934,784453,784602,784614,785381,785688,785768,785859,786468,786487,786490,786496,786667,787627,787770,787985,789389,790405,791041,791184,791194,791224,791243,791326,791328,791789,792740,793372,793757,793882,793981,794082,794673,794822,795043,795152,795210,795457,795466,797168,797425,797596,797607,802727,802940,804462,804544,804734,805153,809131,809603,810916,810977,812125,812137,812432,813001,813013,813866,814180,814708,814876,815972,816252,817442,817822,819339,819361,820110,820132,820874,820954,821397,828196,828201,828210,828225,828759,830378-830379,830999,831106,831774,831785,831828,831850,831860,832214,832218,833121,833545,834047,835036,835336,836405,881396,881412,883130,883134,883146,883165,883177,883362,883565,884341,885038,885231,885241,885260,885991,886019,888072,889363,889606,889716,890139,890265,8903
 49-890350,890417,891185-891187,891583,892198,892341,892415,892464,892555,892812,892814,892817,892887,893321,893493
+/tomcat/trunk:601180,606992,612607,630314,640888,652744,653247,666232,673796,673820,677910,683969,683982,684001,684081,684234,684269-684270,685177,687503,687645,689402,690781,691392,691805,692748,693378,694992,695053,695311,696780,696782,698012,698227,698236,698613,699427,699634,701355,709294,709811,709816,710063,710066,710125,710205,711126,711600,712461,712467,713953,714002,718360,719119,719124,719602,719626,719628,720046,720069,721040,721286,721708,721886,723404,723738,726052,727303,728032,728768,728947,729057,729567,729569,729571,729681,729809,729815,729934,730250,730590,731651,732859,732863,734734,740675,740684,742677,742697,742714,744160,744238,746321,746384,746425,747834,747863,748344,750258,750291,750921,751286-751287,751289,751295,753039,757335,757774,758249,758365,758596,758616,758664,759074,761601,762868,762929,762936-762937,763166,763183,763193,763228,763262,763298,763302,763325,763599,763611,763654,763681,763706,764985,764997,765662,768335,769979,770716,770809,77
 0876,772872,776921,776924,776935,776945,777464,777466,777576,777625,778379,778523-778524,781528,781779,782145,782791,783316,783696,783724,783756,783762,783766,783863,783934,784453,784602,784614,785381,785688,785768,785859,786468,786487,786490,786496,786667,787627,787770,787985,789389,790405,791041,791184,791194,791224,791243,791326,791328,791789,792740,793372,793757,793882,793981,794082,794673,794822,795043,795152,795210,795457,795466,797168,797425,797596,797607,802727,802940,804462,804544,804734,805153,809131,809603,810916,810977,812125,812137,812432,813001,813013,813866,814180,814708,814876,815972,816252,817442,817822,819339,819361,820110,820132,820874,820954,821397,828196,828201,828210,828225,828759,830378-830379,830999,831106,831774,831785,831828,831850,831860,832214,832218,833121,833545,834047,835036,835336,836405,881396,881412,883130,883134,883146,883165,883177,883362,883565,884341,885038,885231,885241,885260,885901,885991,886019,888072,889363,889606,889716,890139,8902
 65,890349-890350,890417,891185-891187,891583,892198,892341,892415,892464,892555,892812,892814,892817,892843,892887,893321,893493

Modified: tomcat/tc6.0.x/trunk/STATUS.txt
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/STATUS.txt (original)
+++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Dec 23 16:31:31 2009
@@ -93,13 +93,6 @@
 
          I won't oppose the patch. I have to think a bit more about it.
 
-* More memory leak protection: ThreadLocals, RMI references, optionally stopping
-  threads.
-  http://svn.apache.org/viewvc?view=revision&revision=885901
-  http://svn.apache.org/viewvc?view=revision&revision=892843
-  +1: markt, jim, kkolinko
-  -1: 
-
 * Fix second part of https://issues.apache.org/bugzilla/show_bug.cgi?id=47413#c8
   Coerce result of composite EL expression (${a}${b}) from String to the expected type.
   The example demonstrating it was added in r892968.

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java Wed Dec 23 16:31:31
2009
@@ -708,6 +708,17 @@
      */
     private boolean useHttpOnly = false;
 
+    /**
+     * Should Tomcat attempt to terminate threads that have been started by the
+     * web application? Stopping threads is performed via the deprecated (for
+     * good reason) <code>Thread.stop()</code> method and is likely to result
in
+     * instability. As such, enabling this should be viewed as an option of last
+     * resort in a development environment and is not recommended in a
+     * production environment. If not specified, the default value of
+     * <code>false</code> will be used. 
+     */
+    private boolean clearReferencesStopThreads = false;
+
     // ----------------------------------------------------- Context Properties
 
 
@@ -2011,6 +2022,33 @@
     }
 
 
+    /**
+     * Return the clearReferencesStopThreads flag for this Context.
+     */
+    public boolean getClearReferencesStopThreads() {
+
+        return (this.clearReferencesStopThreads);
+
+    }
+
+
+    /**
+     * Set the clearReferencesStopThreads feature for this Context.
+     *
+     * @param clearReferencesStopThreads The new flag value
+     */
+    public void setClearReferencesStopThreads(
+            boolean clearReferencesStopThreads) {
+
+        boolean oldClearReferencesStopThreads = this.clearReferencesStopThreads;
+        this.clearReferencesStopThreads = clearReferencesStopThreads;
+        support.firePropertyChange("clearReferencesStopThreads",
+                                   oldClearReferencesStopThreads,
+                                   this.clearReferencesStopThreads);
+
+    }
+
+
     // -------------------------------------------------------- Context Methods
 
 

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties Wed Dec 23
16:31:31 2009
@@ -34,6 +34,11 @@
 webappClassLoader.stopped=Illegal access: this web application instance has been stopped
already.  Could not load {0}.  The eventual following stack trace is caused by an error thrown
for debugging purposes as well as to attempt to terminate the thread which caused the illegal
access, and has no functional impact.
 webappClassLoader.readError=Resource read error: Could not load {0}.
 webappClassLoader.clearJbdc=A web application registered the JBDC driver [{0}] but failed
to unregister it when the web application was stopped. To prevent a memory leak, the JDBC
Driver has been forcibly unregistered.
+webappClassLoader.clearRmiInfo=Failed to find class sun.rmi.transport.Target to clear context
class loader. This is expected on non-Sun JVMs.
+webappClassLoader.clearRmiFail=Failed to clear context class loader referenced from sun.rmi.transport.Target

+webappClassLoader.clearThreadLocal=A web application created a ThreadLocal with key of type
[{0}] (value [{1}]) and a value of type [{2}] (value [{3}]) but failed to remove it when the
web application was stopped. To prevent a memory leak, the ThreadLocal has been forcibly removed.
+webappClassLoader.clearThreadLocalFail=Failed to clear ThreadLocal references
+webappClassLoader.stopThreadFail=Failed to terminate thread named [{0}]
 webappClassLoader.validationErrorJarPath=Unable to validate JAR entry with name {0}
 webappClassLoader.warnThread=A web application appears to have started a thread named [{0}]
but has failed to stop it. This is very likely to create a memory leak.
 webappClassLoader.wrongVersion=(unable to load class {0})

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java Wed Dec 23
16:31:31 2009
@@ -24,7 +24,10 @@
 import java.io.FilePermission;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.ref.Reference;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -45,6 +48,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Vector;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -429,6 +433,17 @@
     protected Permission allPermission = new java.security.AllPermission();
 
 
+    /**
+     * Should Tomcat attempt to terminate threads that have been started by the
+     * web application? Stopping threads is performed via the deprecated (for
+     * good reason) <code>Thread.stop()</code> method and is likely to result
in
+     * instability. As such, enabling this should be viewed as an option of last
+     * resort in a development environment and is not recommended in a
+     * production environment. If not specified, the default value of
+     * <code>false</code> will be used. 
+     */
+    private boolean clearReferencesStopThreads = false;
+
     // ------------------------------------------------------------- Properties
 
 
@@ -594,6 +609,25 @@
          parent = pcl;
      }
 
+     /**
+      * Return the clearReferencesStopThreads flag for this Context.
+      */
+     public boolean getClearReferencesStopThreads() {
+         return (this.clearReferencesStopThreads);
+     }
+
+     
+     /**
+      * Set the clearReferencesStopThreads feature for this Context.
+      *
+      * @param clearReferencesStopThreads The new flag value
+      */
+     public void setClearReferencesStopThreads(
+             boolean clearReferencesStopThreads) {
+         this.clearReferencesStopThreads = clearReferencesStopThreads;
+     }
+
+
     // ------------------------------------------------------- Reloader Methods
 
 
@@ -1672,6 +1706,12 @@
         // Stop any threads the web application started
         clearReferencesThreads();
         
+        // Clear any ThreadLocals loaded by this class loader
+        clearReferencesThreadLocals();
+        
+        // Clear RMI Targets loaded by this class loader
+        clearReferencesRmiTargets();
+
         // Null out any static or final fields from loaded classes,
         // as a workaround for apparent garbage collection bugs
         if (ENABLE_CLEAR_REFERENCES) {
@@ -1877,30 +1917,15 @@
     }
 
 
+    @SuppressWarnings("deprecation")
     private void clearReferencesThreads() {
-        // Get the current thread group 
-        ThreadGroup tg = Thread.currentThread( ).getThreadGroup( );
-        // Find the root thread group
-        while (tg.getParent() != null) {
-            tg = tg.getParent();
-        }
-        
-        int threadCountGuess = tg.activeCount() + 50;
-        Thread[] threads = new Thread[threadCountGuess];
-        int threadCountActual = tg.enumerate(threads);
-        // Make sure we don't miss any threads
-        while (threadCountActual == threadCountGuess) {
-            threadCountGuess *=2;
-            threads = new Thread[threadCountGuess];
-            // Note tg.enumerate(Thread[]) silently ignores any threads that
-            // can't fit into the array 
-            threadCountActual = tg.enumerate(threads);
-        }
+        Thread[] threads = getThreads();
         
         // Iterate over the set of threads
         for (Thread thread : threads) {
             if (thread != null) {
-                if (thread.getContextClassLoader() == this) {
+                ClassLoader ccl = thread.getContextClassLoader();
+                if (ccl != null && ccl == this) {
                     // Don't warn about this thread
                     if (thread == Thread.currentThread()) {
                         continue;
@@ -1912,21 +1937,270 @@
                     }
 
                     // Don't warn about JVM controlled threads
-                    if (thread.getThreadGroup() != null &&
-                            JVM_THREAD_GROUP_NAMES.contains(
-                                    thread.getThreadGroup().getName())) {
+                    ThreadGroup tg = thread.getThreadGroup();
+                    if (tg != null &&
+                            JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
                         continue;
                     }
                    
                     log.error(sm.getString("webappClassLoader.warnThread",
                             thread.getName()));
+                    
+                    // Don't try an stop the threads unless explicitly
+                    // configured to do so
+                    if (!clearReferencesStopThreads) {
+                        continue;
+                    }
+                    
+                    // If the thread has been started via an executor, try
+                    // shutting down the executor
+                    try {
+                        Field targetField =
+                            thread.getClass().getDeclaredField("target");
+                        targetField.setAccessible(true);
+                        Object target = targetField.get(thread);
+                        
+                        if (target != null &&
+                                target.getClass().getCanonicalName().equals(
+                                "java.util.concurrent.ThreadPoolExecutor.Worker")) {
+                            Field executorField =
+                                target.getClass().getDeclaredField("this$0");
+                            executorField.setAccessible(true);
+                            Object executor = executorField.get(target);
+                            if (executor instanceof ThreadPoolExecutor) {
+                                ((ThreadPoolExecutor) executor).shutdownNow();
+                            }
+                        }
+                    } catch (SecurityException e) {
+                        log.warn(sm.getString(
+                                "webappClassLoader.stopThreadFail",
+                                thread.getName()), e);
+                    } catch (NoSuchFieldException e) {
+                        log.warn(sm.getString(
+                                "webappClassLoader.stopThreadFail",
+                                thread.getName()), e);
+                    } catch (IllegalArgumentException e) {
+                        log.warn(sm.getString(
+                                "webappClassLoader.stopThreadFail",
+                                thread.getName()), e);
+                    } catch (IllegalAccessException e) {
+                        log.warn(sm.getString(
+                                "webappClassLoader.stopThreadFail",
+                                thread.getName()), e);
+                    }
+
+                    // This method is deprecated and for good reason. This is
+                    // very risky code but is the only option at this point.
+                    // A *very* good reason for apps to do this clean-up
+                    // themselves.
+                    thread.stop();
+                }
+            }
+        }
+    }
+
+    
+    private void clearReferencesThreadLocals() {
+        Thread[] threads = getThreads();
 
+        try {
+            // Make the fields in the Thread class that store ThreadLocals
+            // accessible
+            Field threadLocalsField =
+                Thread.class.getDeclaredField("threadLocals");
+            threadLocalsField.setAccessible(true);
+            Field inheritableThreadLocalsField =
+                Thread.class.getDeclaredField("inheritableThreadLocals");
+            inheritableThreadLocalsField.setAccessible(true);
+            // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects
+            // accessible
+            Class<?> tlmClass =
+                Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
+            Field tableField = tlmClass.getDeclaredField("table");
+            tableField.setAccessible(true);
+            
+            for (int i = 0; i < threads.length; i++) {
+                Object threadLocalMap;
+                if (threads[i] != null) {
+                    // Clear the first map
+                    threadLocalMap = threadLocalsField.get(threads[i]);
+                    clearThreadLocalMap(threadLocalMap, tableField);
+                    // Clear the second map
+                    threadLocalMap =
+                        inheritableThreadLocalsField.get(threads[i]);
+                    clearThreadLocalMap(threadLocalMap, tableField);
+                }
+            }
+        } catch (SecurityException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (NoSuchFieldException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (ClassNotFoundException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (IllegalArgumentException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (IllegalAccessException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (NoSuchMethodException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        } catch (InvocationTargetException e) {
+            log.warn(sm.getString("webappClassLoader.clearThreadLocalFail"), e);
+        }       
+    }
+
+
+    /*
+     * Clears the given thread local map object. Also pass in the field that
+     * points to the internal table to save re-calculating it on every
+     * call to this method.
+     */
+    private void clearThreadLocalMap(Object map, Field internalTableField)
+            throws NoSuchMethodException, IllegalAccessException,
+            NoSuchFieldException, InvocationTargetException {
+        if (map != null) {
+            Method mapRemove =
+                map.getClass().getDeclaredMethod("remove",
+                        ThreadLocal.class);
+            mapRemove.setAccessible(true);
+            Object[] table = (Object[]) internalTableField.get(map);
+            if (table != null) {
+                for (int j =0; j < table.length; j++) {
+                    if (table[j] != null) {
+                        boolean remove = false;
+                        // Check the key
+                        Field keyField =
+                            Reference.class.getDeclaredField("referent");
+                        keyField.setAccessible(true);
+                        Object key = keyField.get(table[j]);
+                        if (this.equals(key) || (key != null &&
+                                this == key.getClass().getClassLoader())) {
+                            remove = true;
+                        }
+                        // Check the value
+                        Field valueField =
+                            table[j].getClass().getDeclaredField("value");
+                        valueField.setAccessible(true);
+                        Object value = valueField.get(table[j]);
+                        if (this.equals(value) || (value != null &&
+                                this == value.getClass().getClassLoader())) {
+                            remove = true;
+                        }
+                        if (remove) {
+                            Object entry = ((Reference<?>) table[j]).get();
+                            Object[] args = new Object[4];
+                            if (key != null) {
+                                args[0] = key.getClass().getCanonicalName();
+                                args[1] = key.toString();
+                            }
+                            if (value != null) {
+                                args[2] = value.getClass().getCanonicalName();
+                                args[3] = value.toString();
+                            }
+                            log.error(sm.getString(
+                                    "webappClassLoader.clearThreadLocal",
+                                    args));
+                            mapRemove.invoke(map, entry);
+                        }
+                    }
                 }
             }
         }
     }
 
+    /*
+     * Get the set of current threads as an array.
+     */
+    private Thread[] getThreads() {
+        // Get the current thread group 
+        ThreadGroup tg = Thread.currentThread( ).getThreadGroup( );
+        // Find the root thread group
+        while (tg.getParent() != null) {
+            tg = tg.getParent();
+        }
+        
+        int threadCountGuess = tg.activeCount() + 50;
+        Thread[] threads = new Thread[threadCountGuess];
+        int threadCountActual = tg.enumerate(threads);
+        // Make sure we don't miss any threads
+        while (threadCountActual == threadCountGuess) {
+            threadCountGuess *=2;
+            threads = new Thread[threadCountGuess];
+            // Note tg.enumerate(Thread[]) silently ignores any threads that
+            // can't fit into the array 
+            threadCountActual = tg.enumerate(threads);
+        }
+        
+        return threads;
+    }
+
+
+    /**
+     * This depends on the internals of the Sun JVM so it does everything by
+     * reflection.
+     */
+    private void clearReferencesRmiTargets() {
+        try {
+            // Need access to the ccl field of sun.rmi.transport.Target
+            Class<?> objectTargetClass =
+                Class.forName("sun.rmi.transport.Target");
+            Field cclField = objectTargetClass.getDeclaredField("ccl");
+            cclField.setAccessible(true);
+
+            // Clear the objTable map
+            Class<?> objectTableClass =
+                Class.forName("sun.rmi.transport.ObjectTable");
+            Field objTableField = objectTableClass.getDeclaredField("objTable");
+            objTableField.setAccessible(true);
+            Object objTable = objTableField.get(null);
+            if (objTable == null) {
+                return;
+            }
+            
+            // Iterate over the values in the table
+            if (objTable instanceof Map<?,?>) {
+                Iterator<?> iter = ((Map<?,?>) objTable).values().iterator();
+                while (iter.hasNext()) {
+                    Object obj = iter.next();
+                    Object cclObject = cclField.get(obj);
+                    if (this == cclObject) {
+                        iter.remove();
+                    }
+                }
+            }
 
+            // Clear the implTable map
+            Field implTableField = objectTableClass.getDeclaredField("implTable");
+            implTableField.setAccessible(true);
+            Object implTable = implTableField.get(null);
+            if (implTable == null) {
+                return;
+            }
+            
+            // Iterate over the values in the table
+            if (implTable instanceof Map<?,?>) {
+                Iterator<?> iter = ((Map<?,?>) implTable).values().iterator();
+                while (iter.hasNext()) {
+                    Object obj = iter.next();
+                    Object cclObject = cclField.get(obj);
+                    if (this == cclObject) {
+                        iter.remove();
+                    }
+                }
+            }
+        } catch (ClassNotFoundException e) {
+            log.info(sm.getString("webappClassLoader.clearRmiInfo"), e);
+        } catch (SecurityException e) {
+            log.warn(sm.getString("webappClassLoader.clearRmiFail"), e);
+        } catch (NoSuchFieldException e) {
+            log.warn(sm.getString("webappClassLoader.clearRmiFail"), e);
+        } catch (IllegalArgumentException e) {
+            log.warn(sm.getString("webappClassLoader.clearRmiFail"), e);
+        } catch (IllegalAccessException e) {
+            log.warn(sm.getString("webappClassLoader.clearRmiFail"), e);
+        }
+    }
+    
+    
     /**
      * Determine whether a class was loaded by this class loader or one of
      * its child class loaders.

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappLoader.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappLoader.java?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappLoader.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappLoader.java Wed Dec 23 16:31:31
2009
@@ -638,8 +638,12 @@
             classLoader = createClassLoader();
             classLoader.setResources(container.getResources());
             classLoader.setDelegate(this.delegate);
-            if (container instanceof StandardContext)
-                classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());
+            if (container instanceof StandardContext) {
+                classLoader.setAntiJARLocking(
+                        ((StandardContext) container).getAntiJARLocking());
+                classLoader.setClearReferencesStopThreads(
+                        ((StandardContext) container).getClearReferencesStopThreads());
+            }
 
             for (int i = 0; i < repositories.length; i++) {
                 classLoader.addRepository(repositories[i]);

Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Dec 23 16:31:31 2009
@@ -41,6 +41,24 @@
         thread when the web application stops or is reloaded. Failure to stop a
         thread is very likely to result in a memory leak. (markt)
       </add>
+      <add>
+        Provide an option to stop any threads a web application starts but fails
+        to stop when the web application stops or is reloaded. Using this option
+        is very likely to result in instability and should be viewed as a last
+        resort in development and is not recommended at all in production.
+        (markt)
+      </add>
+      <add>
+        Log errors if a web application creates a ThreadLocal but fails to clear
+        it when the web application stops or is reloaded. Failure to clear a
+        ThreadLocal is very likely to result in a memory leak. (markt)
+      </add>
+      <add>
+        Clear any unintentional references remaining in
+        <code>sun.rmi.transport.Target</code> when the web application stops
or
+        is reloaded. Failure to clear these is very likely to result in a memory
+        leak. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">

Modified: tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml?rev=893564&r1=893563&r2=893564&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml Wed Dec 23 16:31:31 2009
@@ -330,6 +330,16 @@
         disclosure, among other security problems.</b></p>
       </attribute>
 
+      <attribute name="clearReferencesStopThreads" required = "false">
+        <p>If <code>true</code>, Tomcat attempts to terminate threads that
have
+        been started by the web application. Stopping threads is performed via
+        the deprecated (for good reason) <code>Thread.stop()</code> method and
+        is likely to result in instability. As such, enabling this should be
+        viewed as an option of last resort in a development environment and is
+        not recommended in a production environment. If not specified, the
+        default value of <code>false</code> will be used.</p>
+      </attribute>
+
       <attribute name="processTlds" required="false">
         <p>Whether the context should process TLDs on startup.  The default
         is true.  The false setting is intended for special cases



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


Mime
View raw message