tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Bill Barker" <billwbar...@verizon.net>
Subject Re: svn commit: r909097 - in /tomcat/trunk/java/org/apache/catalina/loader: LocalStrings.properties WebappClassLoader.java
Date Fri, 12 Feb 2010 02:52:05 GMT


<markt@apache.org> wrote in message 
news:20100211180956.BFD5F23888E2@eris.apache.org...
> Author: markt
> Date: Thu Feb 11 18:09:08 2010
> New Revision: 909097
>
> URL: http://svn.apache.org/viewvc?rev=909097&view=rev
> Log:
> Improved memory leak prevention for resource ResourceBundle
>
> Modified:
>    tomcat/trunk/java/org/apache/catalina/loader/LocalStrings.properties
>    tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
>
> Modified: 
> tomcat/trunk/java/org/apache/catalina/loader/LocalStrings.properties
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/LocalStrings.properties?rev=909097&r1=909096&r2=909097&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/loader/LocalStrings.properties 
> (original)
> +++ tomcat/trunk/java/org/apache/catalina/loader/LocalStrings.properties 
> Thu Feb 11 18:09:08 2010
> @@ -35,6 +35,8 @@
> webappClassLoader.stopped=Illegal access: this web application instance 
> has been stopped already.  Could not load {0}.  The eventual following 
> stack trace is caused by an error thrown for debugging purposes as well as 
> to attempt to terminate the thread which caused the illegal access, and 
> has no functional impact.
> webappClassLoader.readError=Resource read error: Could not load {0}.
> webappClassLoader.clearJbdc=A web application registered the JBDC driver 
> [{0}] but failed to unregister it when the web application was stopped. To 
> prevent a memory leak, the JDBC Driver has been forcibly unregistered.
> +webappClassLoader.clearReferencesResourceBundlesCount=Removed [{0}] 
> ResourceBundle references from the cache
> +webappClassLoader.clearReferencesResourceBundlesFail=Failed to clear 
> ResourceBundle references
> webappClassLoader.clearRmiInfo=Failed to find class 
> sun.rmi.transport.Target to clear context class loader. This is expected 
> on non-Sun JVMs.
> webappClassLoader.clearRmiFail=Failed to clear context class loader 
> referenced from sun.rmi.transport.Target
> webappClassLoader.clearThreadLocalDebug=A web application created a 
> ThreadLocal with key of type [{0}] (value [{1}]). The ThreadLocal has been 
> correctly set to null and the key will be removed by GC. However, to 
> simplify the process of tracing memory leaks, the key has been forcibly 
> removed.
>
> Modified: 
> tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=909097&r1=909096&r2=909097&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
> (original)
> +++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
> Thu Feb 11 18:09:08 2010
> @@ -25,6 +25,7 @@
> import java.io.IOException;
> import java.io.InputStream;
> import java.lang.ref.Reference;
> +import java.lang.ref.WeakReference;
> import java.lang.reflect.Field;
> import java.lang.reflect.InvocationTargetException;
> import java.lang.reflect.Method;
> @@ -47,6 +48,8 @@
> import java.util.LinkedHashMap;
> import java.util.List;
> import java.util.Map;
> +import java.util.ResourceBundle;
> +import java.util.Set;
> import java.util.Vector;
> import java.util.concurrent.ThreadPoolExecutor;
> import java.util.jar.Attributes;
> @@ -66,6 +69,7 @@
> import org.apache.catalina.LifecycleException;
> import org.apache.catalina.LifecycleListener;
> import org.apache.tomcat.util.res.StringManager;
> +import org.apache.jasper.servlet.JasperLoader;
> import org.apache.naming.JndiPermission;
> import org.apache.naming.resources.Resource;
> import org.apache.naming.resources.ResourceAttributes;
> @@ -1773,6 +1777,9 @@
>             org.apache.juli.logging.LogFactory.release(this);
>         }
>
> +        // Clear the resource bundle cache
> +        clearReferencesResourceBundles();
> +
>         // Clear the classloader reference in the VM's bean introspector
>         java.beans.Introspector.flushCaches();
>
> @@ -2313,6 +2320,83 @@
>
>
>     /**
> +     * Clear the {@link ResourceBundle} cache of any bundles loaded by 
> this
> +     * class loader or any class loader where this loader is a parent 
> class
> +     * loader. Whilst {@link ResourceBundle#clearCache()} could be used 
> there
> +     * are complications around the {@link JasperLoader} that mean a 
> reflection
> +     * based approach is more likely to be complete.
> +     *
> +     * The ResourceBundle is using WeakReferences so it shouldn't be 
> pinning the
> +     * class loader in memory. However, it is. Therefore clear ou the
> +     * references.
> +     */
> +    private void clearReferencesResourceBundles() {
> +        // Get a reference to the cache
> +        try {
> +            Field cacheListField =
> +                ResourceBundle.class.getDeclaredField("cacheList");
> +            cacheListField.setAccessible(true);
> +

I don't see anywhere in the documentation that says that there is a 
cacheList field.  Have you tried this with a non-Sun JVM?

> +            // Java 6 uses ConcurrentMap
> +            // Java 5 uses SoftCache extends Abstract Map
> +            // So use Map and it *should* work with both
> +            Map<?,?> cacheList = (Map<?,?>) cacheListField.get(null);
> +
> +            // Get the keys (loader references are in the key)
> +            Set<?> keys = cacheList.keySet();
> +
> +            Field loaderRefField = null;
> +
> +            // Iterate over the keys looking at the loader instances
> +            Iterator<?> keysIter = keys.iterator();
> +
> +            int countRemoved = 0;
> +
> +            while (keysIter.hasNext()) {
> +                Object key = keysIter.next();
> +
> +                if (loaderRefField == null) {
> +                    loaderRefField =
> +                        key.getClass().getDeclaredField("loaderRef");
> +                    loaderRefField.setAccessible(true);
> +                }
> +                WeakReference<?> loaderRef =
> +                    (WeakReference<?>) loaderRefField.get(key);
> +
> +                ClassLoader loader = (ClassLoader) loaderRef.get();
> +
> +                while (loader != null && loader != this) {
> +                    loader = loader.getParent();
> +                }
> +
> +                if (loader != null) {
> +                    keysIter.remove();
> +                    countRemoved++;
> +                }
> +            }
> +
> +            if (countRemoved > 0 && log.isDebugEnabled()) {
> +                log.debug(sm.getString(
> + 
> "webappClassLoader.clearReferencesResourceBundlesCount",
> +                        Integer.valueOf(countRemoved)));
> +            }
> +        } catch (SecurityException e) {
> +            log.error(sm.getString(
> + 
> "webappClassLoader.clearReferencesResourceBundlesFail"), e);
> +        } catch (NoSuchFieldException e) {
> +            log.error(sm.getString(
> + 
> "webappClassLoader.clearReferencesResourceBundlesFail"), e);
> +        } catch (IllegalArgumentException e) {
> +            log.error(sm.getString(
> + 
> "webappClassLoader.clearReferencesResourceBundlesFail"), e);
> +        } catch (IllegalAccessException e) {
> +            log.error(sm.getString(
> + 
> "webappClassLoader.clearReferencesResourceBundlesFail"), e);
> +        }
> +    }
> +
> +
> +    /**
>      * Determine whether a class was loaded by this class loader or one of
>      * its child class loaders.
>      */ 



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


Mime
View raw message