public class ClassLoaderLeakPreventor extends Object implements javax.servlet.ServletContextListener
Activate protection by adding this class as a context listener
in your web.xml, like this:
<listener>
<listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class>
</listener>
You should usually declare this listener before any other listeners, to make it "outermost".
web.xml,
i.e.:
<context-param>
<param-name>ClassLoaderLeakPreventor.stopThreads</param-name>
<param-value>false</param-value>
</context-param>
The available settings are
| Parameter name | Default value | Description |
|---|---|---|
ClassLoaderLeakPreventor.stopThreads |
true |
Should threads tied to the web app classloader be forced to stop at application shutdown? |
ClassLoaderLeakPreventor.stopTimerThreads |
true |
Should Timer threads tied to the web app classloader be forced to stop at application shutdown? |
ClassLoaderLeakPreventor.executeShutdownHooks |
true |
Should shutdown hooks registered from the application be executed at application shutdown? |
ClassLoaderLeakPreventor.threadWaitMs |
5000 (5 seconds) |
No of milliseconds to wait for threads to finish execution, before stopping them. |
ClassLoaderLeakPreventor.shutdownHookWaitMs |
10000 (10 seconds) |
No of milliseconds to wait for shutdown hooks to finish execution, before stopping them. If set to -1 there will be no waiting at all, but Thread is allowed to run until finished. |
This code is licensed under the Apache 2 license, which allows you to include modified versions of the code in your distributed software, without having to release your source code.
For more info, see here.
If you want to help improve this component, you should be aware of the design goals
Primary design goal: Zero dependencies. The component should build and run using nothing but the JDK and the Servlet API. Specifically we should not depend on any logging framework, since they are part of the problem. We also don't want to use any utility libraries, in order not to impose any further dependencies into any project that just wants to get rid of classloader leaks. Access to anything outside of the standard JDK (in order to prevent a known leak) should be managed with reflection.
Secondary design goal: Keep the runtime component in a single .java file. It should be possible to
just add this one .java file into your own source tree.
| Modifier and Type | Class and Description |
|---|---|
protected class |
ClassLoaderLeakPreventor.ClearingThreadLocalProcessor
ThreadLocalProcessor that not only detects and warns about potential leaks, but also tries to clear them
|
protected class |
ClassLoaderLeakPreventor.JURTKiller
Inner class with the sole task of killing JURT finalizer thread after it is done processing jobs.
|
protected static interface |
ClassLoaderLeakPreventor.ThreadLocalProcessor |
protected class |
ClassLoaderLeakPreventor.WarningThreadLocalProcessor
ThreadLocalProcessor that detects and warns about potential leaks
|
| Modifier and Type | Field and Description |
|---|---|
static String |
CAUCHO_TRANSACTION_IMPL
Class name for per thread transaction in Caucho Resin transaction manager
|
protected boolean |
executeShutdownHooks
Should shutdown hooks registered from the application be executed at application shutdown?
|
protected Field |
java_lang_Thread_inheritableThreadLocals |
protected Field |
java_lang_Thread_threadLocals |
protected Field |
java_lang_ThreadLocal$ThreadLocalMap_table |
protected Field |
java_lang_ThreadLocal$ThreadLocalMap$Entry_value |
static String |
JURT_ASYNCHRONOUS_FINALIZER |
protected List<javax.servlet.ServletContextListener> |
otherListeners
Other
ServletContextListeners to use also |
static int |
SHUTDOWN_HOOK_WAIT_MS_DEFAULT
Default no of milliseconds to wait for shutdown hook to finish execution
|
protected int |
shutdownHookWaitMs
No of milliseconds to wait for shutdown hooks to finish execution, before stopping them.
|
protected boolean |
startOracleTimeoutThread
Should the
oracle.jdbc.driver.OracleTimeoutPollingThread thread be forced to start with system classloader,
in case Oracle JDBC driver is present? |
protected boolean |
stopThreads
Should threads tied to the web app classloader be forced to stop at application shutdown?
|
protected boolean |
stopTimerThreads
Should Timer threads tied to the web app classloader be forced to stop at application shutdown?
|
static int |
THREAD_WAIT_MS_DEFAULT
Default no of milliseconds to wait for threads to finish execution
|
protected int |
threadWaitMs
No of milliseconds to wait for threads to finish execution, before stopping them.
|
| Constructor and Description |
|---|
ClassLoaderLeakPreventor() |
| Modifier and Type | Method and Description |
|---|---|
protected void |
clearBeanELResolverCache()
Clean the cache of BeanELResolver
|
protected void |
clearDefaultAuthenticator()
Clear the default java.net.Authenticator (in case current one is loaded in web app).
|
protected void |
clearDefaultProxySelector()
If default
ProxySelector is loaded by web application it needs to be unset |
protected void |
clearIntrospectionUtilsCache()
Clear IntrospectionUtils caches of Tomcat and Apache Commons Modeler
|
protected void |
clearRmiTargetsMap(Map<?,?> rmiTargetsMap)
Iterate RMI Targets Map and remove entries loaded by web app classloader
|
protected void |
clearThreadLocalsOfAllThreads() |
protected RuntimeException |
constructRuntimeExceptionWithoutStackTrace(String message,
Throwable cause)
Construct a new
RuntimeException without any stack trace, in order to avoid any references back to this class |
void |
contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent) |
void |
contextInitialized(javax.servlet.ServletContextEvent servletContextEvent) |
protected AccessControlContext |
createAccessControlContext()
Create
AccessControlContext that is used in contextInitialized(javax.servlet.ServletContextEvent). |
protected void |
debug(String s)
To "turn off" debug logging override this method in a subclass and make that subclass method empty.
|
protected void |
deregisterIIOServiceProvider()
Unregister ImageIO Service Provider loaded by the web application class loader
|
void |
deregisterJdbcDrivers()
Deregister JDBC drivers loaded by web app classloader
|
protected void |
deregisterPropertyEditors()
Deregister custom property editors
|
protected void |
deregisterRmiTargets()
This method is heavily inspired by org.apache.catalina.loader.WebappClassLoader.clearReferencesRmiTargets()
|
protected void |
deregisterSecurityProviders()
Deregister custom security providers
|
protected void |
deregisterShutdownHooks()
Find and deregister shutdown hooks.
|
void |
destroyThreadGroups()
Destroy any ThreadGroups that are loaded by the application classloader
|
protected void |
error(String s)
To "turn off" error logging override this method in a subclass and make that subclass method empty.
|
protected void |
error(Throwable t)
To "turn off" error logging override this method in a subclass and make that subclass method empty.
|
protected Class<?> |
findClass(String className) |
protected Class<?> |
findClass(String className,
boolean trySystemCL) |
protected Field |
findField(Class<?> clazz,
String fieldName) |
protected Field |
findFieldOfClass(String className,
String fieldName) |
protected Field |
findFieldOfClass(String className,
String fieldName,
boolean trySystemCL) |
protected Method |
findMethod(Class<?> clazz,
String methodName,
Class... parameterTypes) |
void |
fixBeanValidationApiLeak() |
protected void |
fixGeoToolsLeak()
Shutdown GeoTools cleaner thread as of https://osgeo-org.atlassian.net/browse/GEOT-2742
|
protected void |
fixJsfLeak()
Workaround for leak caused by Mojarra JSF implementation if included in the container.
|
protected void |
forceStartOpenOfficeJurtCleanup()
The bug detailed at https://issues.apache.org/ooo/show_bug.cgi?
|
protected void |
forEachThreadLocalInCurrentThread(ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor)
Loop ThreadLocals and inheritable ThreadLocals in current Thread
and for each found, invoke the callback interface
|
protected void |
forEachThreadLocalInThread(Thread thread,
ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor) |
protected static void |
gc()
Unlike
this method guarantees that garbage collection has been performed before
returning. |
protected Collection<Thread> |
getAllThreads()
Get a Collection with all Threads.
|
protected Authenticator |
getDefaultAuthenticator()
Find default
Authenticator |
protected <T> T |
getFieldValue(Field field,
Object obj) |
protected <T> T |
getFieldValue(Object obj,
String fieldName) |
protected static int |
getIntInitParameter(javax.servlet.ServletContext servletContext,
String parameterName,
int defaultValue)
Parse init parameter for integer value, returning default if not found or invalid
|
protected String |
getLogPrefix() |
protected <E> E |
getStaticFieldValue(Class<?> clazz,
String fieldName) |
protected <T> T |
getStaticFieldValue(Field field) |
protected <E> E |
getStaticFieldValue(String className,
String fieldName) |
protected <E> E |
getStaticFieldValue(String className,
String fieldName,
boolean trySystemCL) |
protected ClassLoaderLeakPreventor.ThreadLocalProcessor |
getThreadLocalProcessor()
Get
ClassLoaderLeakPreventor.ThreadLocalProcessor to be used. |
protected ClassLoader |
getWebApplicationClassLoader() |
protected void |
info(String s)
To "turn off" info logging override this method in a subclass and make that subclass method empty.
|
protected void |
initAwt()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initDatatypeConverterImpl()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initDocumentBuilderFactory()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initJarUrlConnection()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initJava2dDisposer()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initJavaxSecurityLoginConfiguration()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initJdbcDrivers()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initLdapPoolManager()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initOracleJdbcThread()
See https://github.com/mjiderhamn/classloader-leak-prevention/issues/8
and https://github.com/mjiderhamn/classloader-leak-prevention/issues/23
and http://java.jiderhamn.se/2012/02/26/classloader-leaks-v-common-mistakes-and-known-offenders/
|
protected void |
initSecurityPolicy()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initSecurityProviders()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initSunAwtAppContext()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected void |
initSunGC()
To skip this step override this method in a subclass and make that subclass method empty.
|
protected boolean |
isJBoss()
Override this method if you want to customize how we determine if we're running in
JBoss WildFly (a.k.a JBoss AS).
|
protected boolean |
isJettyWithJMX()
Are we running in Jetty with JMX enabled?
|
protected boolean |
isJvmShuttingDown()
Is the JVM currently shutting down?
|
protected boolean |
isLoadedByWebApplication(Class<?> clazz)
Test if provided class is loaded with web application classloader
|
protected boolean |
isLoadedInWebApplication(Object o)
Test if provided object is loaded with web application classloader
|
protected boolean |
isOracleJRE()
Override this method if you want to customize how we determine if this is a Oracle/Sun
Java Runtime Environment.
|
protected boolean |
isThreadInWebApplication(Thread thread) |
protected boolean |
isWebAppClassLoaderOrChild(ClassLoader cl)
Test if provided ClassLoader is the classloader of the web application, or a child thereof
|
protected void |
processThreadLocalMap(Thread thread,
ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor,
Object threadLocalMap) |
protected void |
removeShutdownHook(Thread shutdownHook)
Deregister shutdown hook and execute it immediately
|
protected void |
removeWrappedAuthenticators(Authenticator authenticator)
Recursively removed wrapped
Authenticator loaded in this webapp. |
protected void |
replaceDOMNormalizerSerializerAbortException()
As reported at https://github.com/mjiderhamn/classloader-leak-prevention/issues/36, invoking
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().normalizeDocument(); or
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
DOMImplementationLS implementation = (DOMImplementationLS)document.getImplementation();
implementation.createLSSerializer().writeToString(document);
may trigger leaks caused by the static fields com.sun.org.apache.xerces.internal.dom.DOMNormalizer#abort and
com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl#abort respectively keeping stacktraces/backtraces
that may include references to classes loaded by our web application. |
protected void |
setFinalStaticField(Field field,
Object newValue) |
protected void |
stopThreads()
Partially inspired by org.apache.catalina.loader.WebappClassLoader.clearReferencesThreads()
|
protected void |
stopTimerThread(Thread thread) |
protected void |
unregisterMBeans()
Unregister MBeans loaded by the web application class loader
|
protected void |
unregisterMXBeanNotificationListeners()
Unregister MBeans loaded by the web application class loader,
and MBean
NotificationListeners loaded by the web application class loader |
protected void |
unregisterNotificationListeners(NotificationBroadcasterSupport mBean)
Unregister
NotificationListeners from subclass of NotificationBroadcasterSupport, if listener,
filter or handback is loaded by the web app classloader. |
protected void |
unsetCachedKeepAliveTimer()
Since Keep-Alive-Timer thread may have terminated, but still be referenced, we need to make sure it does not
reference this classloader.
|
protected void |
waitForThread(Thread thread,
long waitMs)
Make the provided Thread stop sleep(), wait() or join() and then give it the provided no of milliseconds to finish
executing.
|
protected void |
warn(String s)
To "turn off" warn logging override this method in a subclass and make that subclass method empty.
|
protected void |
warn(Throwable t)
To "turn off" warn logging override this method in a subclass and make that subclass method empty.
|
public static final int THREAD_WAIT_MS_DEFAULT
public static final int SHUTDOWN_HOOK_WAIT_MS_DEFAULT
public static final String JURT_ASYNCHRONOUS_FINALIZER
public static final String CAUCHO_TRANSACTION_IMPL
protected boolean stopThreads
protected boolean stopTimerThreads
protected boolean executeShutdownHooks
protected boolean startOracleTimeoutThread
oracle.jdbc.driver.OracleTimeoutPollingThread thread be forced to start with system classloader,
in case Oracle JDBC driver is present? This is normally a good idea, but can be disabled in case the Oracle JDBC
driver is not used even though it is on the classpath.protected int threadWaitMs
protected int shutdownHookWaitMs
protected final Field java_lang_Thread_threadLocals
protected final Field java_lang_Thread_inheritableThreadLocals
protected final Field java_lang_ThreadLocal$ThreadLocalMap_table
protected Field java_lang_ThreadLocal$ThreadLocalMap$Entry_value
protected final List<javax.servlet.ServletContextListener> otherListeners
ServletContextListeners to use alsopublic void contextInitialized(javax.servlet.ServletContextEvent servletContextEvent)
contextInitialized in interface javax.servlet.ServletContextListenerprotected AccessControlContext createAccessControlContext()
AccessControlContext that is used in contextInitialized(javax.servlet.ServletContextEvent).
The motive is to avoid spawned threads from inheriting all the ProtectionDomains of the
running code, since that will include the web app classloader.protected boolean isJBoss()
protected boolean isOracleJRE()
protected void initAwt()
protected void initSecurityProviders()
protected void initJdbcDrivers()
protected void initSunAwtAppContext()
sun.awt.AppContext#contextClassLoader to the classloader of the calls
to sun.awt.AppContext#getAppContext(). Avoid leak by forcing initialization using system classloader.
Note that Google Web Toolkit (GWT) will trigger this leak via its use of javax.imageio.
See http://java.jiderhamn.se/2012/02/26/classloader-leaks-v-common-mistakes-and-known-offenders/protected void initSecurityPolicy()
protected void initDocumentBuilderFactory()
protected void replaceDOMNormalizerSerializerAbortException()
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().normalizeDocument(); or
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
DOMImplementationLS implementation = (DOMImplementationLS)document.getImplementation();
implementation.createLSSerializer().writeToString(document);
may trigger leaks caused by the static fields com.sun.org.apache.xerces.internal.dom.DOMNormalizer#abort and
com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl#abort respectively keeping stacktraces/backtraces
that may include references to classes loaded by our web application.
Since the Throwable.backtrace itself cannot be accessed via reflection (see
http://bugs.java.com/view_bug.do?bug_id=4496456) we need to replace the with new one without any stack trace.protected RuntimeException constructRuntimeExceptionWithoutStackTrace(String message, Throwable cause)
RuntimeException without any stack trace, in order to avoid any references back to this classprotected void initDatatypeConverterImpl()
protected void initJavaxSecurityLoginConfiguration()
protected void initJarUrlConnection()
protected void initLdapPoolManager()
protected void initJava2dDisposer()
protected void initSunGC()
protected void initOracleJdbcThread()
public void contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent)
contextDestroyed in interface javax.servlet.ServletContextListenerprotected void deregisterIIOServiceProvider()
public void deregisterJdbcDrivers()
protected void unregisterMBeans()
protected void unregisterMXBeanNotificationListeners()
NotificationListeners loaded by the web application class loaderprotected void unregisterNotificationListeners(NotificationBroadcasterSupport mBean)
NotificationListeners from subclass of NotificationBroadcasterSupport, if listener,
filter or handback is loaded by the web app classloader.protected void deregisterShutdownHooks()
protected void removeShutdownHook(Thread shutdownHook)
protected void deregisterPropertyEditors()
protected void deregisterSecurityProviders()
protected void clearDefaultAuthenticator()
protected Authenticator getDefaultAuthenticator()
Authenticatorprotected void removeWrappedAuthenticators(Authenticator authenticator)
Authenticator loaded in this webapp. May be needed in case there are multiple CXF
applications within the same container.protected void clearDefaultProxySelector()
ProxySelector is loaded by web application it needs to be unsetprotected void deregisterRmiTargets()
protected void clearRmiTargetsMap(Map<?,?> rmiTargetsMap)
protected void clearThreadLocalsOfAllThreads()
protected ClassLoaderLeakPreventor.ThreadLocalProcessor getThreadLocalProcessor()
ClassLoaderLeakPreventor.ThreadLocalProcessor to be used. Override to customize ThreadLocal processing.protected void stopThreads()
protected void stopTimerThread(Thread thread)
protected void waitForThread(Thread thread, long waitMs)
thread - The thread to wake up and wait forwaitMs - The no of milliseconds to wait. If <= 0 this method does nothing.public void destroyThreadGroups()
protected void unsetCachedKeepAliveTimer()
protected void clearBeanELResolverCache()
public void fixBeanValidationApiLeak()
protected void fixJsfLeak()
protected void fixGeoToolsLeak()
protected void clearIntrospectionUtilsCache()
protected void forceStartOpenOfficeJurtCleanup()
protected ClassLoader getWebApplicationClassLoader()
protected boolean isLoadedInWebApplication(Object o)
protected boolean isLoadedByWebApplication(Class<?> clazz)
protected boolean isWebAppClassLoaderOrChild(ClassLoader cl)
protected boolean isThreadInWebApplication(Thread thread)
protected <E> E getStaticFieldValue(String className, String fieldName, boolean trySystemCL)
protected Field findFieldOfClass(String className, String fieldName, boolean trySystemCL)
protected <T> T getStaticFieldValue(Field field)
protected boolean isJvmShuttingDown()
protected Collection<Thread> getAllThreads()
protected void forEachThreadLocalInCurrentThread(ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor)
protected void forEachThreadLocalInThread(Thread thread, ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor)
protected void processThreadLocalMap(Thread thread, ClassLoaderLeakPreventor.ThreadLocalProcessor threadLocalProcessor, Object threadLocalMap) throws IllegalAccessException
IllegalAccessExceptionprotected static int getIntInitParameter(javax.servlet.ServletContext servletContext,
String parameterName,
int defaultValue)
protected static void gc()
System.gc() this method guarantees that garbage collection has been performed before
returning.protected String getLogPrefix()
protected void debug(String s)
protected void info(String s)
protected void warn(String s)
warn(Throwable).protected void warn(Throwable t)
warn(String).protected void error(String s)
error(Throwable).protected void error(Throwable t)
error(String).protected boolean isJettyWithJMX()
Copyright © 2016. All rights reserved.