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