How to identify and remove Threads/ThreadLocals initiated from our webapp in Java?

孤人 提交于 2019-12-02 23:40:54

There is no solution to fix all threadlocal leaks in one go. Normally third party libraries using Threadlocal variables have some kind of cleanup API call which can be used to clear their local thread variables.

You have to check for all reported threadlocal leaks and find the correct way of disposing them in the corresponding library. You can do this in your CustomServletContextListener

examples:

log4j (javadoc):

LogManager.shutdown()

jdbc driver: (javadoc):

DriverManager.deregisterDriver(driver);

note: Also check for new versions of your 3rd party libs to check fox fixes concerning memory leaks (and/or thread local leaks).

Alexey Shurygin

Solution depends on the library that created these Threads/ThreadLocal-s. Basically you need to call library's cleanup code from your CustomServletContextListener.contextDestroyed() method.

So find what is the library and how to shut it down properly.

You can try this code to remove all ThreadLocal

private void cleanThreadLocals() {
    try {
        // Get a reference to the thread locals table of the current thread
        Thread thread = Thread.currentThread();
        Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
        threadLocalsField.setAccessible(true);
        Object threadLocalTable = threadLocalsField.get(thread);

        // Get a reference to the array holding the thread local variables inside the
        // ThreadLocalMap of the current thread
        Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
        Field tableField = threadLocalMapClass.getDeclaredField("table");
        tableField.setAccessible(true);
        Object table = tableField.get(threadLocalTable);

        // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
        // is a reference to the actual ThreadLocal variable
        Field referentField = Reference.class.getDeclaredField("referent");
        referentField.setAccessible(true);

        for (int i=0; i < Array.getLength(table); i++) {
            // Each entry in the table array of ThreadLocalMap is an Entry object
            // representing the thread local reference and its value
            Object entry = Array.get(table, i);
            if (entry != null) {
                // Get a reference to the thread local object and remove it from the table
                ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
                threadLocal.remove();
            }
        }
    } catch(Exception e) {
        // We will tolerate an exception here and just log it
        throw new IllegalStateException(e);
    }
}

If we remove objects from thread local of all threads while stopping the container then we are only trying to solve the problem of error messages which are displayed by container. Rather, the objective should be to avoid memory leaks which may occurr over the period of time when container is not stopped/restarted. So, ideally as threads from ThreadPool are reused to serve different requests then after response is sent then there should not be any reason to hold memory by keeping objects in thread local because that thread may be used to serve next (totally different) request from client. One of the suggestion is to remove any objects from thread local by configuring Filter which is executed immediately before response is sent from server.

I would try to find which library causes these ThreadLocal, possibly by running the web app in a debugger and stopping on ThreadLocal creation. Then you can see if you forgot to clean up behind some library or if a library is buggy/not made for web app use. Maybe post your findings here.

When cleaning threads in a context listener, I once did check that the contextClassLoader of the thread was the same as the thread running the listener, to avoid messing the threads of other applications.

Hope this helps.

try

Runtime.getRuntime().addShutdownHook(webapp.new ShutdownHook());

in your shudownhook, clean the objects

If you use ThreadLocal in your code you could replace that ThreadLocal with ImrpovedThreadLocal that I made and you will not have a memory leak on stop/redeploy. You can use that threadLocal in the same way without having any thread contention.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!