Author: markt Date: Wed Feb 24 00:07:06 2010 New Revision: 915603 URL: http://svn.apache.org/viewvc?rev=915603&view=rev Log: Add basic memory leak detection for JMX and manager (kkolinko/markt)
Modified: tomcat/tc6.0.x/trunk/STATUS.txt tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml Modified: tomcat/tc6.0.x/trunk/STATUS.txt URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/STATUS.txt (original) +++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Feb 24 00:07:06 2010 @@ -118,36 +118,6 @@ http://tools.ietf.org/html/rfc2616 - -* Memory leak detection for JMX and manager - http://people.apache.org/~kkolinko/patches/2010-02-16_tc6_memory-leak-detection.patch - It is corrected markt's patch - (Added revs.909204,910584,910612 - TC 6 specifics: /findleaks should be explicitly mapped in web.xml, - where TC 7 uses /text/findleaks, and that URL is mentioned in manager-howto.xml) - +1: kkolinko, fhanik, markt - -1: - - kkolinko: Some further thoughts on naming/messages/documentation - - It might be not so clear to a newcomer, that to use this functionality you have - a) Use you application - b) Reload it - c) Press the "Find leaks" button - It does not work without a) and b). - - - Saying that application "triggered" a memory leak sounds too harsh for - me. I think "has" or "suffered" would be better here. - - - It is safe to press the "Find leaks" button several times. Besides the - gc call it just "lists" the contexts that are still present in memory. - It does not clear its own list. - - kkolinko: The display fix for ROOT context (r910612) might be applied to - StandardHost.findReloadedContextMemoryLeaks() as well. Any comments? If - results of a JMX call are displayed to humans, I'd be better to have "/" - instead of an empty string. - - * Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=48371 Take account of comments when working out where to insert generated web.xml http://people.apache.org/~markt/patches/2010-02-13-bug48371.patch Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java Wed Feb 24 00:07:06 2010 @@ -19,14 +19,23 @@ package org.apache.catalina.core; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.Valve; +import org.apache.catalina.loader.WebappClassLoader; import org.apache.catalina.startup.HostConfig; import org.apache.catalina.valves.ValveBase; import org.apache.tomcat.util.modeler.Registry; @@ -164,6 +173,12 @@ */ private boolean xmlNamespaceAware = false; + /** + * Track the class loaders for the child web applications so memory leaks + * can be detected. + */ + private Map<ClassLoader, String> childClassLoaders = + new WeakHashMap<ClassLoader, String>(); // ------------------------------------------------------------- Properties @@ -520,6 +535,11 @@ */ public void addChild(Container child) { + if (child instanceof Lifecycle) { + ((Lifecycle) child).addLifecycleListener( + new MemoryLeakTrackingListener()); + } + if (!(child instanceof Context)) throw new IllegalArgumentException (sm.getString("standardHost.notContext")); @@ -529,6 +549,49 @@ /** + * Used to ensure the regardless of {...@link Context} implementation, a record + * is kept of the class loader used every time a context starts. + */ + private class MemoryLeakTrackingListener implements LifecycleListener { + + public void lifecycleEvent(LifecycleEvent event) { + if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { + if (event.getSource() instanceof Context) { + Context context = ((Context) event.getSource()); + childClassLoaders.put(context.getLoader().getClassLoader(), + context.getServletContext().getContextPath()); + } + } + } + } + + + /** + * Attempt to identify the contexts that have a class loader memory leak. + * This is usually triggered on context reload. Note: This method attempts + * to force a full garbage collection. This should be used with extreme + * caution on a production system. + */ + public String[] findReloadedContextMemoryLeaks() { + + System.gc(); + + List<String> result = new ArrayList<String>(); + + for (Map.Entry<ClassLoader, String> entry : + childClassLoaders.entrySet()) { + ClassLoader cl = entry.getKey(); + if (cl instanceof WebappClassLoader) { + if (!((WebappClassLoader) cl).isStarted()) { + result.add(entry.getValue()); + } + } + } + + return result.toArray(new String[result.size()]); + } + + /** * Return the set of alias names for this Host. If none are defined, * a zero length array is returned. */ Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml Wed Feb 24 00:07:06 2010 @@ -542,6 +542,12 @@ <operation name="stop" description="Stop" impact="ACTION" returnType="void" /> <operation name="init" description="Init" impact="ACTION" returnType="void" /> <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" /> + + <operation name="findReloadedContextMemoryLeaks" + description="Provide a list of contexts that have leaked memory on reload. This will attempt to force a full garbage collection. Use with extreme caution on production systems." + impact="ACTION" + returnType="[Ljava.lang.String;" /> + </mbean> <mbean name="StandardHostValve" 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=915603&r1=915602&r2=915603&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 Wed Feb 24 00:07:06 2010 @@ -1642,6 +1642,10 @@ } + public boolean isStarted() { + return started; + } + /** * Stop the class loader. * Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java Wed Feb 24 00:07:06 2010 @@ -136,6 +136,8 @@ message = start(path); } else if (command.equals("/stop")) { message = stop(path); + } else if (command.equals("/findleaks")) { + message = findleaks(); } else { message = sm.getString("managerServlet.unknownCommand", command); @@ -498,6 +500,16 @@ args[3] = sm.getString("htmlManagerServlet.deployButton"); writer.print(MessageFormat.format(UPLOAD_SECTION, args)); + // Diagnostics section + args = new Object[5]; + args[0] = sm.getString("htmlManagerServlet.diagnosticsTitle"); + args[1] = sm.getString("htmlManagerServlet.diagnosticsLeak"); + args[2] = response.encodeURL(request.getContextPath() + + "/html/findleaks"); + args[3] = sm.getString("htmlManagerServlet.diagnosticsLeakWarning"); + args[4] = sm.getString("htmlManagerServlet.diagnosticsLeakButton"); + writer.print(MessageFormat.format(DIAGNOSTICS_SECTION, args)); + // Server Header Section args = new Object[7]; args[0] = sm.getString("htmlManagerServlet.serverTitle"); @@ -631,6 +643,33 @@ return stringWriter.toString(); } + + /** + * Find potential memory leaks caused by web application reload. + * + * @see ManagerServlet#findleaks(PrintWriter) + * + * @return message String + */ + protected String findleaks() { + + StringBuilder msg = new StringBuilder(); + + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + super.findleaks(printWriter); + + if (stringWriter.getBuffer().length() > 0) { + msg.append(sm.getString("htmlManagerServlet.findleaksList")); + msg.append(stringWriter.toString()); + } else { + msg.append(sm.getString("htmlManagerServlet.findleaksNone")); + } + + return msg.toString(); + } + /** * @see javax.servlet.Servlet#getServletInfo() @@ -1136,4 +1175,31 @@ "<br>\n" + "\n"; + private static final String DIAGNOSTICS_SECTION = + "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" + + "<tr>\n" + + " <td colspan=\"2\" class=\"title\">{0}</td>\n" + + "</tr>\n" + + "<tr>\n" + + " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" + + "</tr>\n" + + "<tr>\n" + + " <td colspan=\"2\">\n" + + "<form method=\"post\" action=\"{2}\">\n" + + "<table cellspacing=\"0\" cellpadding=\"3\">\n" + + "<tr>\n" + + " <td class=\"row-left\">\n" + + " <input type=\"submit\" value=\"{4}\">\n" + + " </td>\n" + + " <td class=\"row-left\">\n" + + " <small>{3}</small>\n" + + " </td>\n" + + "</tr>\n" + + "</table>\n" + + "</form>\n" + + "</td>\n" + + "</tr>\n" + + "</table>\n" + + "<br>"; + } Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties Wed Feb 24 00:07:06 2010 @@ -43,6 +43,12 @@ htmlManagerServlet.deployUploadNoFile=FAIL - File upload failed, no file htmlManagerServlet.deployUploadWarExists=FAIL - War file \"{0}\" already exists on server htmlManagerServlet.deployWar=WAR or Directory URL: +htmlManagerServlet.diagnosticsLeak=Check to see if a web application has caused a memory leak on reload +htmlManagerServlet.diagnosticsLeakButton=Find leaks +htmlManagerServlet.diagnosticsLeakWarning=This diagnostic check will trigger a full garbage collection. Use it with extreme caution on production systems. +htmlManagerServlet.diagnosticsTitle=Diagnostics +htmlManagerServlet.findleaksList=The following web applications appear to have triggered a memory leak on reload (use a profiler to confirm):\n +htmlManagerServlet.findleaksNone=No web applications appear to have triggered a memory leak on reload. htmlManagerServlet.list=List Applications htmlManagerServlet.manager=Manager htmlManagerServlet.messageLabel=Message: @@ -63,6 +69,7 @@ managerServlet.deployFailed=FAIL - Failed to deploy application at context path {0} managerServlet.deployedButNotStarted=FAIL - Deployed application at context path {0} but context failed to start managerServlet.exception=FAIL - Encountered exception {0} +managerServlet.findleaksFail=FAIL - Find leaks failed: Host not instance of StandardHost managerServlet.invalidPath=FAIL - Invalid context path {0} was specified managerServlet.invalidWar=FAIL - Invalid application URL {0} was specified managerServlet.listed=OK - Listed applications for virtual host {0} Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java Wed Feb 24 00:07:06 2010 @@ -54,6 +54,7 @@ import org.apache.catalina.Session; import org.apache.catalina.UserDatabase; import org.apache.catalina.Wrapper; +import org.apache.catalina.core.StandardHost; import org.apache.catalina.core.StandardServer; import org.apache.catalina.util.RequestUtil; import org.apache.catalina.util.ServerInfo; @@ -379,6 +380,8 @@ stop(writer, path); } else if (command.equals("/undeploy")) { undeploy(writer, path); + } else if (command.equals("/findleaks")) { + findleaks(writer); } else { writer.println(sm.getString("managerServlet.unknownCommand", command)); @@ -523,6 +526,28 @@ /** + * Find potential memory leaks caused by web application reload. + */ + protected void findleaks(PrintWriter writer) { + + if (!(host instanceof StandardHost)) { + writer.println(sm.getString("managerServlet.findleaksFail")); + return; + } + + String[] results = + ((StandardHost) host).findReloadedContextMemoryLeaks(); + + for (String result : results) { + if ("".equals(result)) { + result = "/"; + } + writer.println(result); + } + } + + + /** * Store server configuration. * * @param path Optional context path to save 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=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Feb 24 00:07:06 2010 @@ -108,6 +108,11 @@ Modify ThreadLocal memory leak detection to not report false positives and to simplify implementation. (markt/kkolinko) </update> + <add> + Basic memory leak detection was added to the standard Host + implementation and exposed via JMX to detect memory leaks on web + application reload. (markt/kkolinko) + </add> </changelog> </subsection> <subsection name="Jasper"> @@ -150,6 +155,14 @@ </fix> </changelog> </subsection> + <subsection name="Webapps"> + <changelog> + <add> + Make the new web application reload memory leak detection available + through the Manager application. (markt/kkolinko) + </add> + </changelog> + </subsection> <subsection name="Other"> <changelog> <fix> Modified: tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml Wed Feb 24 00:07:06 2010 @@ -36,7 +36,7 @@ Tomcat. This document is for the HTML web interface to the web application <a href="manager-howto.html">manager</a>.</p> -<p>The interface is divided into five sections: +<p>The interface is divided into six sections: <ul> <li><strong>Message</strong> - Displays success and failure messages.</li> <li><strong>Manager</strong> - General manager operations like list and @@ -44,6 +44,7 @@ <li><strong>Applications</strong> - List of web applications and commands.</li> <li><strong>Deploy</strong> - Deploying web applications.</li> + <li><strong>Diagnostics</strong> - Identifying potential problems.</li> <li><strong>Server Information</strong> - Information about the Tomcat server.</li> </ul> @@ -546,6 +547,19 @@ </subsection> </section> +<section name="Diagnostics"> + +<p><strong>The find leaks diagnostic triggers a full garbage collection. It +should be used with extreme caution on production systems.</strong></p> + +<p>The find leaks diagnostic attempts to identify web applications that have +caused memory leaks when they were reloaded. Results should always be confirmed +with a profiler. The diagnostic uses additional functionality provided by the +StandardHost implementation. It will not work if a custom host is used that +does not extend StandardHost.</p> + +</section> + <section name="Server Information"> <p>This section displays information about Tomcat, the operating system of Modified: tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml Wed Feb 24 00:07:06 2010 @@ -51,6 +51,8 @@ <a href="#Stop an Existing Application">Stop an Existing Application</a><br /> <a href="#Undeploy an Existing Application"> Undeploy an Existing Application</a><br /> +<a href="#Server Status">Server Status</a><br /> +<a href="#Finding memory leaks">Finding memory leaks</a><br /> </blockquote> <a href="#Executing Manager Commands With Ant"> Executing Manager Commands With Ant</a><br /> @@ -897,6 +899,35 @@ </subsection> +<subsection name="Finding memory leaks"> + +<source> +http://localhost:8080/manager/findleaks +</source> + +<p><strong>The find leaks diagnostic triggers a full garbage collection. It +should be used with extreme caution on production systems.</strong></p> + +<p>The find leaks diagnostic attempts to identify web applications that have +caused memory leaks when they were reloaded. Results should always be confirmed +with a profiler. The diagnostic uses additional functionality provided by the +StandardHost implementation. It will not work if a custom host is used that +does not extend StandardHost.</p> + +<p>If this command succeeds, you will see a response like this:</p> +<source> +/leaking-webapp +</source> + +<p>Each context path for a web application that is believed to have triggered a +memory leak when it was reloaded will be listed on a new line. If an application +has been reloaded several times, it may be listed several times.</p> + +<p>If the command does not succeed, the response will start with +<code>FAIL</code> and include an error message.</p> + +</subsection> + <subsection name="Server Status"> <p>From this link , you can view information about the server.</p> Modified: tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml?rev=915603&r1=915602&r2=915603&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml Wed Feb 24 00:07:06 2010 @@ -121,6 +121,10 @@ <url-pattern>/resources</url-pattern> </servlet-mapping> <servlet-mapping> + <servlet-name>Manager</servlet-name> + <url-pattern>/findleaks</url-pattern> + </servlet-mapping> + <servlet-mapping> <servlet-name>Status</servlet-name> <url-pattern>/status/*</url-pattern> </servlet-mapping> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org