You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by kk...@apache.org on 2012/07/02 14:04:12 UTC

svn commit: r1356198 - in /tomcat/tc6.0.x/trunk: STATUS.txt java/org/apache/catalina/loader/WebappClassLoader.java webapps/docs/changelog.xml

Author: kkolinko
Date: Mon Jul  2 12:04:11 2012
New Revision: 1356198

URL: http://svn.apache.org/viewvc?rev=1356198&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=52850
Extend memory leak prevention and detection code to
work with IBM as well as Oracle JVMs. Based on a patch provided by Rohit Kelapure.

Modified:
    tomcat/tc6.0.x/trunk/STATUS.txt
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
    tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml

Modified: tomcat/tc6.0.x/trunk/STATUS.txt
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=1356198&r1=1356197&r2=1356198&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/STATUS.txt (original)
+++ tomcat/tc6.0.x/trunk/STATUS.txt Mon Jul  2 12:04:11 2012
@@ -127,13 +127,6 @@ PATCHES PROPOSED TO BACKPORT:
   +1: kkolinko
   -1:
 
-* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=52850
-  Extend memory leak prevention and detection code to
-  work with IBM as well as Oracle JVMs. Based on patch provided by Rohit Kelapure.
-  https://issues.apache.org/bugzilla/attachment.cgi?id=28893
-  +1: kkolinko, schultz, markt
-  -1:
-
 * Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=53047
   Allow database realms configured with an all roles mode that is authentication
   only to not have to define a role table

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=1356198&r1=1356197&r2=1356198&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 Mon Jul  2 12:04:11 2012
@@ -2177,8 +2177,9 @@ public class WebappClassLoader
                     }
 
                     // TimerThread can be stopped safely so treat separately
-                    if (thread.getClass().getName().equals(
-                            "java.util.TimerThread") &&
+                    // "java.util.TimerThread" in Sun/Oracle JDK
+                    // "java.util.Timer$TimerImpl" in Apache Harmony and in IBM JDK
+                    if (thread.getClass().getName().startsWith("java.util.Timer") &&
                             clearReferencesStopTimerThreads) {
                         clearReferencesStopTimerThread(thread);
                         continue;
@@ -2201,13 +2202,29 @@ public class WebappClassLoader
                     // 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);
-                        
+                        // Runnable wrapped by Thread
+                        // "target" in Sun/Oracle JDK
+                        // "runnable" in IBM JDK
+                        // "action" in Apache Harmony
+                        Object target = null;
+                        for (String fieldName : new String[] { "target",
+                                "runnable", "action" }) {
+                            try {
+                                Field targetField = thread.getClass()
+                                        .getDeclaredField(fieldName);
+                                targetField.setAccessible(true);
+                                target = targetField.get(thread);
+                                break;
+                            } catch (NoSuchFieldException nfe) {
+                                continue;
+                            }
+                        }
+
+                        // "java.util.concurrent" code is in public domain,
+                        // so all implementations are similar
                         if (target != null &&
-                                target.getClass().getCanonicalName().equals(
+                                target.getClass().getCanonicalName() != null
+                                && target.getClass().getCanonicalName().equals(
                                 "java.util.concurrent.ThreadPoolExecutor.Worker")) {
                             Field executorField =
                                 target.getClass().getDeclaredField("this$0");
@@ -2276,37 +2293,46 @@ public class WebappClassLoader
     
     
     private void clearReferencesStopTimerThread(Thread thread) {
-        
+
         // Need to get references to:
-        // - newTasksMayBeScheduled field
+        // in Sun/Oracle JDK:
+        // - newTasksMayBeScheduled field (in java.util.TimerThread)
         // - queue field
         // - queue.clear()
-        
+        // in IBM JDK, Apache Harmony:
+        // - cancel() method (in java.util.Timer$TimerImpl)
+
         try {
-            Field newTasksMayBeScheduledField =
-                thread.getClass().getDeclaredField("newTasksMayBeScheduled");
-            newTasksMayBeScheduledField.setAccessible(true);
-            Field queueField = thread.getClass().getDeclaredField("queue");
-            queueField.setAccessible(true);
-    
-            Object queue = queueField.get(thread);
-            
-            Method clearMethod = queue.getClass().getDeclaredMethod("clear");
-            clearMethod.setAccessible(true);
-            
-            synchronized(queue) {
-                newTasksMayBeScheduledField.setBoolean(thread, false);
-                clearMethod.invoke(queue);
-                queue.notify();  // In case queue was already empty.
+
+            try {
+                Field newTasksMayBeScheduledField =
+                    thread.getClass().getDeclaredField("newTasksMayBeScheduled");
+                newTasksMayBeScheduledField.setAccessible(true);
+                Field queueField = thread.getClass().getDeclaredField("queue");
+                queueField.setAccessible(true);
+
+                Object queue = queueField.get(thread);
+
+                Method clearMethod = queue.getClass().getDeclaredMethod("clear");
+                clearMethod.setAccessible(true);
+
+                synchronized(queue) {
+                    newTasksMayBeScheduledField.setBoolean(thread, false);
+                    clearMethod.invoke(queue);
+                    queue.notify();  // In case queue was already empty.
+                }
+
+            }catch (NoSuchFieldException nfe){
+                Method cancelMethod = thread.getClass().getDeclaredMethod("cancel");
+                synchronized(thread) {
+                    cancelMethod.setAccessible(true);
+                    cancelMethod.invoke(thread);
+                }
             }
-            
+
             log.error(sm.getString("webappClassLoader.warnTimerThread",
                     contextName, thread.getName()));
 
-        } catch (NoSuchFieldException e) {
-            log.warn(sm.getString(
-                    "webappClassLoader.stopTimerThreadFail",
-                    thread.getName(), contextName), e);
         } catch (IllegalAccessException e) {
             log.warn(sm.getString(
                     "webappClassLoader.stopTimerThreadFail",
@@ -2340,17 +2366,27 @@ public class WebappClassLoader
                 Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
             Field tableField = tlmClass.getDeclaredField("table");
             tableField.setAccessible(true);
-            
+            Method expungeStaleEntriesMethod = tlmClass.getDeclaredMethod("expungeStaleEntries");
+            expungeStaleEntriesMethod.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);
+                    if (null != threadLocalMap){
+                        expungeStaleEntriesMethod.invoke(threadLocalMap);
+                        checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+                    }
+
                     // Clear the second map
                     threadLocalMap =
                         inheritableThreadLocalsField.get(threads[i]);
-                    clearThreadLocalMap(threadLocalMap, tableField);
+                    if (null != threadLocalMap){
+                        expungeStaleEntriesMethod.invoke(threadLocalMap);
+                        checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+                    }
                 }
             }
         } catch (SecurityException e) {
@@ -2383,7 +2419,7 @@ public class WebappClassLoader
      * points to the internal table to save re-calculating it on every
      * call to this method.
      */
-    private void clearThreadLocalMap(Object map, Field internalTableField)
+    private void checkThreadLocalMapForLeaks(Object map, Field internalTableField)
             throws NoSuchMethodException, IllegalAccessException,
             NoSuchFieldException, InvocationTargetException {
         if (map != null) {

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=1356198&r1=1356197&r2=1356198&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Mon Jul  2 12:04:11 2012
@@ -124,6 +124,11 @@
         <code>java.lang.String</code>. (markt)
       </fix>
       <add>
+        <bug>52850</bug>: Extend memory leak prevention and detection code to
+        work with IBM as well as Oracle JVMs. Based on a patch provided by
+        Rohit Kelapure. (kkolinko)
+      </add>
+      <add>
         <bug>52996</bug>: In <code>StandardThreadExecutor</code>:
         Add the ability to configure a job queue size
         (<code>maxQueueSize</code> attribute).



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