tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache Wiki <wikidi...@apache.org>
Subject [Tomcat Wiki] Update of "MemoryLeakProtection" by SylvainLaurent
Date Sun, 21 Mar 2010 21:33:27 GMT
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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message