Всякий раз, когда я останавливаю или повторно развертываю webapp, я вижу много ошибок, похожих на,
msg=The web application [] created a ThreadLocal with key of type [] (value []) and
a value of type [] (value []) but failed to remove it when the web application was
stopped. Threads are going to be renewed over time to try and avoid probable memory leak
Я не создаю ThreadLocals в своем приложении, но ссылаюсь на многие библиотеки, которые могут создавать эти ThreadLocals. В настоящее время мы используем Tomcat 7. Я уже рассмотрел другие подобные вопросы [Утечка памяти при повторном развертывании приложения в Tomcat или Что эти предупреждения в Catalina.out?], но все они только предполагают, что это функция Tomcat, чтобы предупредить вас о том, что ThreadLocals не удаляется. Я не вижу ответа на удаление ThreadLocals. Я также вижу несколько ОШИБКИ относительно потока, который не остановлен,
msg=The web application [] appears to have started a thread named [] but has
failed to stop it. This is very likely to create a memory leak.
Они регистрируются как ERROR в нашей центральной системе регистрации компании и тем самым увеличивают количество ошибок в нашем приложении. Это, конечно, не очень хорошо, когда мы проверяем производительность нашего приложения. Я попробовал реализации из этих двух источников [Убийство потоков и Пример кода из this thread], но, похоже, не работает. Он удаляет thread/threadlocals, не созданные нашим приложением. Мне нужно удалить только потоки /threadlocals, запущенные нашим webapp. Есть ли способ удалить их в методе contextDestroyed() ServletContextListener? Ниже приведен мой текущий класс ServletContextListener,
public class CustomServletContextListener implements ServletContextListener {
private List<String> threadsAtStartup;
@Override
public void contextInitialized(ServletContextEvent sce) {
retrieveThreadsOnStartup();
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Now deregister JDBC drivers in this context ClassLoader:
// Get the webapp ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// Loop through all drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver.getClass().getClassLoader() == cl) {
// This driver was registered by the webapp ClassLoader, so deregister it:
try {
System.out.println("Deregistering JDBC driver {}: " + driver);
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
System.out.println("Error deregistering JDBC driver {}: " + driver + "\nException: " + ex);
}
} else {
// driver was not registered by the webapp ClassLoader and may be in use elsewhere
System.out.println("Not deregistering JDBC driver {} as it does not belong to this webapp ClassLoader: " + driver);
}
}
//Threads
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
threadGroup = Thread.currentThread().getThreadGroup();
Thread[] threads;
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e.getMessage());
return;
} catch (IllegalAccessException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e.getMessage());
return;
}
int toBeKilledCount = 0;
int totalThreadCount = 0;
int killedTLCount = 0;
int totalTLCount = 0;
int killedITLCount = 0;
int totalITLCount = 0;
for (; totalThreadCount < threads.length; totalThreadCount++) {
Thread thread = threads[totalThreadCount];
if(thread != null) {
String threadName = thread.getName();
boolean shouldThisThreadBeKilled;
shouldThisThreadBeKilled = isThisThreadToBeKilled(Thread.currentThread(), thread);
if (shouldThisThreadBeKilled) {
//ThreadLocal
try {
removeThreadLocals("threadLocals", thread);
removeThreadLocals("inheritableThreadLocals", thread);
} catch (Exception e) {
System.out.println("\tError accessing threadLocals field of '" + threadName + "': " + e.getMessage());
}
//Stop thread
thread.interrupt();
thread = null;
toBeKilledCount++;
}
}
}
}
private void retrieveThreadsOnStartup() {
final Thread[] threads;
final ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e);
threadsAtStartup = new ArrayList<String>();
return;
} catch (IllegalAccessException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e);
threadsAtStartup = new ArrayList<String>();
return;
}
threadsAtStartup = new ArrayList<String>(threads.length);
for (int i = 0; i < threads.length; i++) {
final Thread thread;
try {
thread = threads[i];
if (null != thread) {
threadsAtStartup.add(thread.getName());
}
} catch (RuntimeException e) {
System.out.println("An error occured on initial Thread statement: " + e);
}
}
}
private Thread[] retrieveCurrentActiveThreads(ThreadGroup threadGroup) throws NoSuchFieldException, IllegalAccessException {
final Thread[] threads;
final Field privateThreadsField;
privateThreadsField = ThreadGroup.class.getDeclaredField("childrenThreads");
privateThreadsField.setAccessible(true);
threads = (Thread[]) privateThreadsField.get(threadGroup);
return threads;
}
private void removeThreadLocals(String fieldName, Thread thread) {
Field threadLocalsField = Thread.class.getDeclaredField(fieldName);
threadLocalsField.setAccessible(true);
Object threadLocalMap = threadLocalsField.get(thread);
Field tableField = threadLocalMap.getClass().getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalMap);
int count = 0;
for (int i = 0, length = Array.getLength(table); i < length; ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
totalTLCount++;
Object threadLocal = ((WeakReference)entry).get();
if (threadLocal != null) {
Array.set(table, i, null);
killedTLCount++;
}
}
}
}
private Boolean isThisThreadToBeKilled(Thread currentThread, Thread testThread) {
boolean toBeKilled;
String currentThreadName = currentThread.getName();
String testThreadName = testThread.getName();
System.out.println("currentThreadName: " + currentThreadName + ", testThreadName: " + testThreadName);
return !threadsAtStartup.contains(testThreadName) // this thread was not already running at startup
&& !testThreadName.equalsIgnoreCase(currentThreadName); // this is not the currently running thread
}
}
Обновление: Я все еще не могу это решить. Любая помощь? Никто никогда не сталкивался с этим?