Org. apache. catalina. loader. WebappClassLoaderBase. clearReferencesJdbc The web application [xx] has an exception,
Exception details:
31-Jan-2018 16:30:40.843 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [spicy_dev] registered the JDBC driver [com.alibaba.druid.proxy.DruidDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.31-Jan-2018 16:30:40.843 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [spicy_dev] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.31-Jan-2018 16:30:40.846 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [spicy_dev] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread: java.lang.Object.wait(Native Method) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)
The above marked red is two important points: in catalina. jar under the lib directory of tomcat
Specifically, the WebappClassLoaderBase class is used to disable some information. The specific code is as follows:
protected void clearReferences() { clearReferencesJdbc(); clearReferencesThreads(); checkThreadLocalsForLeaks(); if (this.clearReferencesRmiTargets) { clearReferencesRmiTargets(); } IntrospectionUtils.clear(); if (this.clearReferencesLogFactoryRelease) { LogFactory.release(this); } Introspector.flushCaches(); TomcatURLStreamHandlerFactory.release(this); } private final void clearReferencesJdbc() { byte[] classBytes = new byte[2048]; int offset = 0; try { InputStream is = getResourceAsStream("org/apache/catalina/loader/JdbcLeakPrevention.class"); Throwable localThrowable2 = null; try { int read = is.read(classBytes, offset, classBytes.length - offset); while (read > -1) { offset += read; if (offset == classBytes.length) { byte[] tmp = new byte[classBytes.length * 2]; System.arraycopy(classBytes, 0, tmp, 0, classBytes.length); classBytes = tmp; } read = is.read(classBytes, offset, classBytes.length - offset); } Class lpClass = defineClass("org.apache.catalina.loader.JdbcLeakPrevention", classBytes, 0, offset, getClass().getProtectionDomain()); Object obj = lpClass.getConstructor(new Class[0]).newInstance(new Object[0]); List driverNames = (List)obj.getClass().getMethod("clearJdbcDriverRegistrations", new Class[0]).invoke(obj, new Object[0]); for (String name : driverNames) log.warn(sm.getString("webappClassLoader.clearJdbc", new Object[] { getContextName(), name })); } catch (Throwable localThrowable1) { localThrowable2 = localThrowable1; throw localThrowable1; } finally { if (is != null) if (localThrowable2 != null) try { is.close(); } catch (Throwable x2) { localThrowable2.addSuppressed(x2); } else is.close(); } } catch (Exception e) { Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(t); log.warn(sm.getString("webappClassLoader.jdbcRemoveFailed", new Object[] { getContextName() }), t); } } private void clearReferencesThreads() { Thread[] threads = getThreads(); List executorThreadsToStop = new ArrayList(); for (Thread thread : threads) { if (thread != null) { ClassLoader ccl = thread.getContextClassLoader(); if (ccl != this) continue; if (thread == Thread.currentThread()) { continue; } String threadName = thread.getName();
Specifically, it is used to close some connection operations. Next we will see it uses the referenced next important class JdbcLeakPrevention.
package org.apache.catalina.loader;import java.sql.Driver;import java.sql.DriverManager;import java.sql.SQLException;import java.util.ArrayList;import java.util.Enumeration;import java.util.HashSet;import java.util.List;public class JdbcLeakPrevention{ public List
clearJdbcDriverRegistrations() throws SQLException { List driverNames = new ArrayList(); HashSet originalDrivers = new HashSet(); Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { originalDrivers.add(drivers.nextElement()); } drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = (Driver)drivers.nextElement(); if (driver.getClass().getClassLoader() != getClass().getClassLoader()) { continue; } if (originalDrivers.contains(driver)) { driverNames.add(driver.getClass().getCanonicalName()); } DriverManager.deregisterDriver(driver); } return driverNames; }}
The code is not long, specifically to remove the database driver. The problem occurs here. Our exception is sent to com. mysql. jdbc. AbandonedConnectionCleanupThread. run (AbandonedConnectionCleanupThread. java: 43 ),
However, there is still a shutdown method in this class that is not used, so one thread is constantly querying database connections, and the other thread is shutting down database connections, therefore, the above problems are caused. (If you want to know which thread has been communicating with the database, check java. lang. ref. referenceQueue class)
Okay, let alone the solution. The idea is to use a ServletContextListener listener to use the com. mysql. jdbc. AbandonedConnectionCleanupThread. shutdown method in the destruction method.
Solution: (here I am using the annotation of servlet3.0)
Package com. spicy. common. framework. listener; import java. SQL. driver; import java. SQL. driverManager; import java. SQL. SQLException; import java. util. enumeration; import javax. servlet. servletContextEvent; import javax. servlet. servletContextListener; import javax. servlet. annotation. webListener; import com. mysql. jdbc. abandonedConnectionCleanupThread; import org. slf4j. logger; import org. slf4j. loggerFactory; @ WebListenerpublic class ContextFinalizer implements ServletContextListener {private Logger logger = LoggerFactory. getLogger (this. getClass (); public void contextInitialized (ServletContextEvent sce) {} public void contextDestroyed (ServletContextEvent sce) {Enumeration
Drivers = DriverManager. getDrivers (); Driver d = null; try {while (drivers. hasMoreElements () {d = drivers. nextElement (); DriverManager. deregisterDriver (d); logger.info ("Remove database connection Driver -->: Driver {} deregistered", d) ;}} catch (SQLException ex) {logger. error ("Error: deregistering driver {} exceptionName: {} detail: {}", d, ex. getClass (). getName (), ex. getMessage ();} finally {try {AbandonedConnectionCleanupThread. shutdown ();} catch (InterruptedException e) {logger. error ("Error: SEVERE problem cleaning up:" + e. getMessage ());}}}}
Result:
31-Jan-2018 17:26:33. 211 information [main] org. apache. catalina. core. standardServer. await A valid shutdown command was has ed via the shutdown port. stopping the Server instance.31-Jan-2018 17:26:33. 211 information [main] org. apache. coyote. abstractProtocol. pause Pausing ProtocolHandler ["http-nio-8080"] 31-Jan-2018 17:26:33. 590 information [main] org. apache. coyote. abstractProtocol. pause Pausing ProtocolHandler ["ajp-nio-8009"] 31-Jan-2018 17:26:33. 939 information [main] org. apache. catalina. core. standardService. stopInternal Stopping service [Catalina] 23907 INFO [17:26:33] Closing WebApplicationContext for namespace 'spring4mvc-servlet ': startup date [Wed Jan 31 17:26:18 CST 2018]; parent: root WebApplicationContext23947 INFO [17:26:33] Eliminate the database connection Driver -->: Driver com. alibaba. druid. proxy. druidDriver @ 2e5aa1e6 deregistered23947 INFO [17:26:33] Remove the database connection Driver -->: Driver com. mysql. jdbc. driver @ 6eaed403 deregisteredlog4j: WARN No appenders cocould be found for logger (org. springframework. web. context. support. xmlWebApplicationContext ). log4j: WARN Please initialize the log4j system properly. log4j: WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.31-Jan-2018 17:26:34. 029 information [main] org. apache. coyote. abstractProtocol. stop Stopping ProtocolHandler ["http-nio-8080"] 31-Jan-2018 17:26:34. 033 information [main] org. apache. coyote. abstractProtocol. stop Stopping ProtocolHandler ["ajp-nio-8009"] 31-Jan-2018 17:26:34. 035 information [main] org. apache. coyote. abstractProtocol. destroy Destroying ProtocolHandler ["http-nio-8080"] 31-Jan-2018 17:26:34. 037 information [main] org. apache. coyote. abstractProtocol. destroy Destroying ProtocolHandler ["ajp-nio-8009"] Disconnected from server