org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application
but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
SEVERE: The web application [xxxxx] appears to have started a thread named [pool-4-thread-x] but has failed to stop it. This is very likely to create a memory leak.
Feb 28, 2015 9:45:04 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
没有了,还剩下一些ThreadLocal无法释放以及剩下一些Thread无法释放的警告
查资料
http://stackoverflow.com/questions/5292349/is-this-very-likely-to-create-a-memory-leak-in-tomcat
回复中给出了ThreadLocal的解决方案,使用下面方法解决即可:
public Integer immolate() { int count = 0; try { final Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); threadLocalsField.setAccessible(true); final Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals"); inheritableThreadLocalsField.setAccessible(true);
//就是说 在tomcat容器里,每个线程的ClassLoader归根结底都被默认设置成webapp ClassLoader。当某些线程无法及时关闭时,webapp classloader就会因为这些线程拥有的强引用,无法正常gc,因此报警。
了解了这个,就有办法了
for (final Thread thread : Thread.getAllStackTraces().keySet()) { count += clear(threadLocalsField.get(thread)); count += clear(inheritableThreadLocalsField.get(thread)); if (thread != null) { thread.setContextClassLoader(null); } } log.info("immolated " + count + " values in ThreadLocals"); } catch (Exception e) { throw new Error("ThreadLocalImmolater.immolate()", e); } return count; } private int clear(final Object threadLocalMap) throws Exception { if (threadLocalMap == null) return 0; int count = 0; final Field tableField = threadLocalMap.getClass().getDeclaredField("table"); tableField.setAccessible(true); final Object table = tableField.get(threadLocalMap); for (int i = 0, length = Array.getLength(table); i < length; ++i) { final Object entry = Array.get(table, i); if (entry != null) { final Object threadLocal = ((WeakReference)entry).get(); if (threadLocal != null) { log(i, threadLocal); Array.set(table, i, null); ++count; } } } return count; }
根据答复中的代码来看,ThreadLocal的报错 估计是某些线程的ThreadLocal无法释放,为什么无法释放,因为那些线程还没停掉,每个ThreadLocal都是被一个Thread的ThreadMap下以<ThreadLocalObject, Object>的entry形式维护着,
这些entry继承了WeakReference,以上代码应该是将每个thread的threadMap的entry设成null,这样原来的entry没有引用源,作为一个WeakReference会在GC中被清除掉。
调用处代码:
while (!executorService.awaitTermination(100, TimeUnit.MILLISECONDS)){ BootStrap.log.info("线程还在执行!!!!!!!"); if(null !=executorService){ executorService.shutdownNow(); immolate(); } executorService=null; break; }
这样这些还没停止的线程的上下文ClassLoader就和webapp ClassLoader无关了,webapp ClassLoader可以正常GC,报错消失。