[I'm re-sending this e-mail as I did not have any response. Maybe it was because of html format ?]
Hello,
Among the many possible causes of classloader leaks, one is the  
context class loader of threads spawned during the execution of web  
applications.
For instance, if the following servlet is executed at least once and  
it is the first time the Graphics2D part of the JRE is used for the  
current JVM, then the web app's classloader will never be garbage- 
collected.
public class MyTomcatServlet extends HttpServlet {

        @Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
                        throws ServletException, IOException {
                System.out.println("MyServlet.doGet : current CCL:"
                                + 
Thread.currentThread().getContextClassLoader());
                        BufferedImage image = new BufferedImage(20, 20,
                                        BufferedImage.TYPE_INT_RGB);
                        Graphics2D g = image.createGraphics();
                        response.setContentType("image/png");
                        OutputStream out = response.getOutputStream();
                        ImageIO.write(image, "png", out);
                        out.close();
                        out.flush();
                        g.dispose();
        }
}

In order to work around such leaks, I created an "ExpendableClassLoader", which will leak instead of the normal WebAppClassLoader, But the former loads no class at all.
Here is the code :

/**
 * <p>
* A special ClassLoader to work around classloader (and thus permgen) leaks * caused by never-ending threads spawned during the execution of the web app. * If no care is taken, such threads have the webapp's classloader as context * classloader. So, if such a thread is still alive after the application is * undeployed, the application's classloader cannot be garbage- collected.
 * </p>
 * <p>
 * When the application is undeployed, the reference to the delegated
 * classloader is nullified so that the latter can be garbage-collected
 * (provided it is not held by other means).
 * </p>
 * <p>
* As of march 2009, there are several such "leaking" threads in Sun's JRE * library : Java2DDisposer {...@link see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6489540 }, LDAP connection pool manager... A very simple way
 * of provoking such a leak is with the following servlet code :
 *
 * <pre>
 * public class MyServlet extends HttpServlet {
 *
 *      &#064;Override
* protected void doGet(HttpServletRequest req, HttpServletResponse response)
 *                      throws ServletException, IOException {
 *              BufferedImage image = new BufferedImage(20, 20,
 *                              BufferedImage.TYPE_INT_RGB);
 *              Graphics2D g = image.createGraphics();
 *              response.setContentType(&quot;image/png&quot;);
 *              OutputStream out = response.getOutputStream();
 *              ImageIO.write(image, &quot;png&quot;, out);
 *              out.close();
 *              out.flush();
 *              g.dispose();
 *      }
 * }
 * </pre>
 *
 * </p>
 * <p>
* By using this ExpendableClassLoader as the webapp's classloader (and thus the * context classloader), such dangling threads only keep a reference to a very
 * light classloader, drastically reducing the leak.
 * </p>
 * <p>
* NOTE: the class has to be in the same package as {...@link WebappClassLoader} in
 * order to override package-protected methods.
 * </p>
 *
 * @author Sylvain LAURENT
 *
 */
public class ExpendableClassLoader extends WebappClassLoader {

        private WebappClassLoader delegatedClassLoader;

        public ExpendableClassLoader(ClassLoader parentClassLoader) {
                delegatedClassLoader = new WebappClassLoader(parentClassLoader);
        }

        public void stop() throws LifecycleException {
                ClassLoader savedParentClassLoader = 
delegatedClassLoader.getParent();

                delegatedClassLoader.stop();

                // release reference to the delegated classloader, potentially
                // sacrificing the current instance
                delegatedClassLoader = null;

                // recreate a delegated classloader, just in case a dangling 
Thread
                // (e.g. JDK threads like Java2Disposer) needs to load a class
                // we use the same parent CL as the previous delegate
                delegatedClassLoader = new 
WebappClassLoader(savedParentClassLoader);

        }

        public String toString() {
                return "ExpendableClassLoader@" + System.identityHashCode(this)
                                + " delegates to " + 
delegatedClassLoader.toString();
        }

        /* ------------------- delegated methods --------------------- */
//skipped for brevity
}

I tested it with a small webapp and a custom context.xml to override the default WebAppClassLoader and it does its job of avoiding this type of memory leak.
I'd be glad to read your opinion on this hack !

Sylvain

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

Reply via email to