https://bz.apache.org/bugzilla/show_bug.cgi?id=58486

            Bug ID: 58486
           Summary: JreMemoryLeakPreventionListener: initialize two
                    further JRE classes
           Product: Tomcat 8
           Version: trunk
          Hardware: PC
            Status: NEW
          Severity: minor
          Priority: P2
         Component: Catalina
          Assignee: dev@tomcat.apache.org
          Reporter: lukewoodwar...@yahoo.co.uk

I would like to propose adding a further couple of classes to those that
Tomcat's JreMemoryLeakPreventionListener statically initializes.

The classes com.sun.org.apache.xerces.internal.dom.DOMNormalizer and
com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl, both within
rt.jar, each contain a static final field of type RuntimeException named
'abort'.  When these classes are statically initialized, these exceptions are
created and their stacktraces filled in.  If a web app class happens to be in
the call stack when either class's exception is created, this class cannot then
be garbage collected when the web app is stopped because an exception in a
static field of a class has a reference to it.  This then causes a PermGen leak
as the web apps's classloader, and all of the classes it loaded, cannot be
garbage-collected.

To reproduce this issue, use the following servlet class:

package com.example;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;

public class DOMNormalizerLeakServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException {
        try {
            Document document =
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            document.createElement("test");
            DOMImplementationLS implementation =
(DOMImplementationLS)document.getImplementation();
            implementation.createLSSerializer().writeToString(document);
            response.getWriter().write("done");
        }
        catch (Exception e) {
            throw new ServletException(e);
        }
    }
}

and the following web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee";
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd";
     version="3.1">
    <servlet>
        <servlet-name>test</servlet-name>
        <servlet-class>com.example.DOMNormalizerLeakServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/index.html</url-pattern>
    </servlet-mapping>   
</web-app>

I've reproduced this problem with the latest Tomcat (8.0.27) and the latest
Oracle JDK8 (1.8.0_60), by:

* deploying a web app consisting of the above servlet class and deployment
descriptor to Tomcat,
* viewing the index.html page generated by Tomcat (the browser should show the
word 'done'),
* reloading the web app using the Tomcat manager app,
* clicking the 'Find leaks' button in the Diagnostics section of the manager
app, which reveals a possible memory leak,
* using a profiler such as JVisualVM to confirm that Tomcat now has a
'destroyed' classloader that could not be garbage collected because there is a
chain of references from a JRE class to the servlet class it loaded.

Here's a path from the classloader to the exception, which I obtained with the
help of JVisualVM:

this     - value: org.apache.catalina.loader.WebappClassLoader #3
 <- <classLoader>     - class: com.example.DOMNormalizerLeakServlet, value:
org.apache.catalina.loader.WebappClassLoader #3
  <- [2]     - class: java.lang.Object[], value:
com.example.DOMNormalizerLeakServlet class DOMNormalizerLeakServlet
   <- [2]     - class: java.lang.Object[], value: java.lang.Object[] #4319
    <- backtrace     - class: java.lang.RuntimeException, value:
java.lang.Object[] #4318
     <- abort (sticky class)     - class:
com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl, value:
java.lang.RuntimeException #1

There is a straightforward workaround for this: add the names of these two
classes to the classesToInitialize attribute for the
JreMemoryLeakPreventionListener.  This then causes the classes to be statically
initialized by Tomcat itself and keeps web app classes out of the stacktrace of
these exceptions.

I have filed a bug report with Oracle to change the behaviour of these two
classes.  However, until this gets fixed (if it gets fixed at all), it would be
appreciated if the JreMemoryLeakPreventionListener could be adapted to handle
these two classes.

-- 
You are receiving this mail because:
You are the assignee for the bug.

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

Reply via email to