tomcat内存泄露问题解决,线程池无法关闭问题

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下以的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,报错消失。

你可能感兴趣的:(tomcat,spring,性能优化)