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=6&rev2=7 -------------------------------------------------- Now, if we uncomment the line {{{leakingThread.setContextClassLoader(null);}}} in the above example, tomcat (6.0.24) no longer detect the leak when the application is stopped because the spawned thread context classloader is no longer the webapp's. (the "Find leaks" feature in the manager will report it though) === Threads spawned by classes loaded by the common classloader === + Suppose, we have the [[http://commons.apache.org/pool/|Commons Pool]] library in the classpath of the server (e.g. the jar is in tomcat/lib), and the following servlet : + {{{ + public class LeakingServlet extends HttpServlet { + private GenericObjectPool objectPool; - TODO : example with the Evictor thread of dbcp + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + response.getWriter().println("Number of idle objects in the pool :"+objectPool.getNumIdle()); + } + + @Override + public void init() throws ServletException { + objectPool = new GenericObjectPool(); + objectPool.setFactory(new BasePoolableObjectFactory() { + private AtomicInteger counter = new AtomicInteger(0); + + @Override + public Object makeObject() throws Exception { + String str = "Object #" + counter.incrementAndGet(); + System.out.println("Creating "+str); + return str; + } + + @Override + public void destroyObject(Object obj) throws Exception { + System.out.println("Destroying "+obj); + } + }); + objectPool.setMinIdle(3); + objectPool.setTimeBetweenEvictionRunsMillis(1000); + } + + @Override + public void destroy() { + try { + objectPool.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }}} + + The call to {{{GenericObjectPool.setTimeBetweenEvictionRunsMillis)}}} actually starts or reuses a {{{java.lang.Timer}}} shared between all {{{GenericObjectPool}}} instances. As long as a pool is running and at least one pool is using the timer eviction feature, the Timer lives. + + If there's no other webapp using commons-pool, there's no leak : when we stop the webapp, the servlet is stopped and the pool is closed. If it was the only pool in use, the Timer thread is also stopped and there's no leak. + + Now, imagine that there are 2 webapps using commons-pool with the timer eviction feature (imagine the above servlet is deployed in 2 webapps A and B). + Suppose webapp A is deployed, then B. Since the commons-pool jar is shared between both webapps, only one Timer thread is spawned, with 2 {{{TimerTask}}}, one for each webapp, to handle the eviction of each pool instance. + + Then, if we stop webapp A, boom it's leaking! the Timer thread has its context classloader set to the {{{WebAppClassLoader}}} of webapp A. This is somehow a [[http://issues.apache.org/jira/browse/POOL-161|bug of commons-pool]], but tomcat 6.0.24 tries to help : + + {{{ + INFO: HTMLManager: stop: Stopping web application at '/testWeb' + Destroying Object #3 + Destroying Object #2 + Destroying Object #1 + Mar 21, 2010 9:26:36 PM org.apache.catalina.loader.WebappClassLoader clearReferencesStopTimerThread + SEVERE: A web application appears to have started a TimerThread named [Timer-0] via the java.util.Timer API but has failed to stop it. To prevent a memory leak, the timer (and hence the associated thread) has been forcibly cancelled. + }}} + + So the leak is fixed, but unfortunately there's a side effect : it broke webapp B eviction timer. + + (TODO: file a bug report to make the clearReferencesStopTimerThread optional and disabled by default) + + Note: as of 6.0.24, by default tomcat stops threads of class {{{java.util.TimerThread}}} whose context classloader is the {{{WebAppClassLoader}}} of the app being stopped. It does not stop other threads, it only warns about them. It can try to stop them if the {{{clearReferencesStopThreads}}} option of the standard context is set to true. === Threads spawned by JRE classes === + Just like third-party libraries may spawn threads that provoke leaks, some JRE classes also spawn threads that inherit from the current class loader and thus provoke leaks. + + Instead of trying to stop such threads, tomcat prefers to force the creation of such threads when the container is started, before webapps are started. The {{{JreMemoryLeakPreventionListener}}} does it for a few known offenders in the JRE. + == Child classloaders == + When an app is stopped, Tomcat 6.0.24 detects leaks caused by {{{ThreadLocal}}}s and Threads context classloader only by checking for the current {{{WebAppClassLoader}}}. If a child classloader is involved, the leak is not detected. That should be [[https://issues.apache.org/bugzilla/show_bug.cgi?id=48837|improved in a future release]]. + == static class variables == + + When an app is stopped, Tomcat (even before 6.0.24) nullifies the value of all static class variables of classes loaded by the {{{WebAppClassLoader}}}. In some cases, it may fix a classloader leak (for example because of a custom {{{ThreadLocal}}} class, see above), but even if we still have a leak, it may decrease the amount of memory lost: + + Imagine a class with the following variable : + {{{ + private final static byte[] BUFFER = new byte[1024*1024]; //1MB buffer + }}} + + Normally, the 1MB buffer should be freed when the app is stopped, but only if the classloader itself can be garbage-collected. + Since there are still possibilities to have a leak of the classloader, clearing the BUFFER variable allows to recover 1MB of memory. == LogFactory == --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org