From 8f2cf8f89db7f4165049b151d93d2588fd47dbc7 Mon Sep 17 00:00:00 2001 From: markt Date: Mon, 8 Feb 2010 12:57:08 +0000 Subject: [PATCH] Add a test case for TimerThread thread/memory leaks on reload as I have some ideas on how to safely terminate those threads git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@907646 13f79535-47bb-0310-9956-ffa450edef68 --- .../loader/TestWebappClassLoaderMemoryLeak.java | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java diff --git a/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java b/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java new file mode 100644 index 000000000..5534e5644 --- /dev/null +++ b/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java @@ -0,0 +1,94 @@ +package org.apache.catalina.loader; + +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestWebappClassLoaderMemoryLeak extends TomcatBaseTest { + + public void testTimerThreadLeak() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + // Must have a real docBase - just use temp + StandardContext ctx = + tomcat.addContext("/", System.getProperty("java.io.tmpdir")); + + Tomcat.addServlet(ctx, "taskServlet", new TaskServlet()); + ctx.addServletMapping("/", "taskServlet"); + + tomcat.start(); + + // This will trigger the timer & thread creation + getUrl("http://localhost:" + getPort() + "/"); + + // Stop the context + ctx.stop(); + + // If the thread still exists, we have a thread/memory leak + Thread[] threads = getThreads(); + for (Thread thread : threads) { + if (thread.getName().equals(TaskServlet.TIMER_THREAD_NAME)) { + fail("Timer thread still running"); + } + } + } + + /* + * Get the set of current threads as an array. + * Copied from WebappClassLoader + */ + private Thread[] getThreads() { + // Get the current thread group + ThreadGroup tg = Thread.currentThread( ).getThreadGroup( ); + // Find the root thread group + while (tg.getParent() != null) { + tg = tg.getParent(); + } + + int threadCountGuess = tg.activeCount() + 50; + Thread[] threads = new Thread[threadCountGuess]; + int threadCountActual = tg.enumerate(threads); + // Make sure we don't miss any threads + while (threadCountActual == threadCountGuess) { + threadCountGuess *=2; + threads = new Thread[threadCountGuess]; + // Note tg.enumerate(Thread[]) silently ignores any threads that + // can't fit into the array + threadCountActual = tg.enumerate(threads); + } + + return threads; + } + + private static final class TaskServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final String TIMER_THREAD_NAME = "leaked-thread"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + Timer timer = new Timer(TIMER_THREAD_NAME); + timer.schedule(new LocalTask(), 0, 10000); + } + + } + + private static final class LocalTask extends TimerTask { + + @Override + public void run() { + // Doesn't actually need to do anything. + } + + } +} -- 2.11.0