Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tomcat Wiki" for change 
notification.

The "MemoryLeakProtection" page has been changed by SylvainLaurent.
http://wiki.apache.org/tomcat/MemoryLeakProtection?action=diff&rev1=3&rev2=4

--------------------------------------------------

  
  The leak is caused because we have a custom class for the {{{ThreadLocal}}} 
instance, and also a custom class for the value bound to the Thread. Actually 
the important thing is that both classes were loaded by the webapp classloader.
  
- Hopefully tomcat 6.0.24 can detect the leak when the application is stopped: 
each Thread in the JVM is examined, and the internal structures of the Thread 
and {{{ThreadLocal}}} classes are introspected to see if either the 
{{{ThreadLocal}}} instance of the value bound to it were loaded by the 
{{{WebAppClassLoader}}} of the application being stopped.
+ Hopefully tomcat 6.0.24 can detect the leak when the application is stopped: 
each Thread in the JVM is examined, and the internal structures of the Thread 
and {{{ThreadLocal}}} classes are introspected to see if either the 
{{{ThreadLocal}}} instance or the value bound to it were loaded by the 
{{{WebAppClassLoader}}} of the application being stopped.
  
  In this particular case, the leak is detected, a message is logged and 
internal structures of the JDK ({{{ThreadLocalMap}}}) are modified to remove 
the reference to the {{{ThreadLocal}}} instance.
  
@@ -78, +78 @@

  
  === Webapp class instance as ThreadLocal value ===
  
+ Suppose that we have the following class in the common classpath (for 
instance in a jar in tomcat/lib) :
+ {{{
+ public class ThreadScopedHolder {
+       private final static ThreadLocal<Object> threadLocal = new 
ThreadLocal<Object>();
+ 
+       public static void saveInHolder(Object o) {
+               threadLocal.set(o);
+       }
+ 
+       public static Object getFromHolder() {
+               return threadLocal.get();
+       }
+ }
+ }}}
+ 
+ And those 2 classes in the webapp :
+ {{{
+ public class MyCounter {
+       private int count = 0;
+ 
+       public void increment() {
+               count++;
+       }
+ 
+       public int getCount() {
+               return count;
+       }
+ }
+ public class LeakingServlet extends HttpServlet {
+ 
+       protected void doGet(HttpServletRequest request,
+                       HttpServletResponse response) throws ServletException, 
IOException {
+ 
+               MyCounter counter = 
(MyCounter)ThreadScopedHolder.getFromHolder();
+               if (counter == null) {
+                       counter = new MyCounter();
+                       ThreadScopedHolder.saveInHolder(counter);
+               }
+ 
+               response.getWriter().println(
+                               "The current thread served this servlet " + 
counter.getCount()
+                                               + " times");
+               counter.increment();
+       }
+ }
+ }}}
+ 
+ If the servlet is invoked at least once, the webapp classloader would not be 
GCed when the app is stopped: since the classloader of {{{ThreadScopedHolder}}} 
is the common classloader, it remains forever which is as expected. But its 
{{{ThreadLocal}}} instance has a value bound to it (for the non-terminated 
thread(s) that served the sevlet), which is an instance of a class loaded by 
the webapp classloader...
+ 
+ Here again, tomcat 6.0.24 will detect and fix the leak by removing this 
{{{ThreadLocal}}} reference from each Thread.
+ {{{
+ Mar 17, 2010 10:23:13 PM org.apache.catalina.loader.WebappClassLoader 
clearThreadLocalMap
+ SEVERE: A web application created a ThreadLocal with key of type 
[java.lang.ThreadLocal] (value [java.lang.threadlo...@44676e3f]) and a value of 
type [test.leak.threadlocal.value.MyCounter] (value 
[test.leak.threadlocal.value.mycoun...@62770d2e]) but failed to remove it when 
the web application was stopped. To prevent a memory leak, the ThreadLocal has 
been forcibly removed.
+ }}}
+ 
+ 
  === Webapp class instance indirectly held through a ThreadLocal value ===
+ Suppose we have the same {{{ThreadScopedHolder}}} class (in the common 
classloader) and {{{MyCounter}}} class in the webapp, but with the following 
servlet :
+ {{{
+ public class LeakingServlet extends HttpServlet {
+ 
+       protected void doGet(HttpServletRequest request,
+                       HttpServletResponse response) throws ServletException, 
IOException {
+ 
+               List<MyCounter> counterList = (List<MyCounter>) 
ThreadScopedHolder
+                               .getFromHolder();
+               MyCounter counter;
+               if (counterList == null) {
+                       counter = new MyCounter();
+                       ThreadScopedHolder.saveInHolder(Arrays.asList(counter));
+               } else {
+                       counter = counterList.get(0);
+               }
+ 
+               response.getWriter().println(
+                               "The current thread served this servlet " + 
counter.getCount()
+                                               + " times");
+               counter.increment();
+       }
+ }
+ }}}
+ 
+ We have more or less the same kind of leak as the previous one, but this time 
tomcat 6.0.24 does not detect the leak when stopping the application (and does 
not fix it). The problem is that when it inspects the entries of 
{{{ThreadLocalMap}}}, it checks whether either the key or the value is an 
instance of a class loaded by the webapp classloader. Here the key is an 
instance of {{{ThreadLocal}}}, and the value is an instance of 
java.util.{{{ArrayList}}}.
+ 
+ The "Find leaks" button in tomcat manager will report the leak when asked :
+ {{{
+ The following web applications were stopped (reloaded, undeployed), but their
+ classes from previous runs are still loaded in memory, thus causing a memory
+ leak (use a profiler to confirm):
+ /testWeb
+ }}}
+ 
+ But it does not give any clue about what caused the leak, we would need to 
make a heapdump and analyse it with some tool like 
[[http://www.eclipse.org/mat|Eclipse MAT]].
  
  == ThreadLocal pseudo-leak ==
  

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

Reply via email to