And*_*dyT 27 java multithreading thread-local tomcat7
每当我停止或重新部署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
Run Code Online (Sandbox Code Playgroud)
我不是在我的应用程序中创建任何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.
Run Code Online (Sandbox Code Playgroud)
这些在我们公司的中央记录系统中作为ERROR登录,从而增加了我们的应用程序的错误数量.当我们检查应用程序的性能时,这肯定不太好看.我尝试了这两个来源的实现[杀死此线程中的线程和示例代码],但似乎不起作用.它删除了我们的应用程序未创建的线程/ threadlocals.我需要的是只删除我们的webapp启动的线程/ threadlocals.我们有什么方法可以在ServletContextListener的contextDestroyed()方法中删除它们吗?以下是我当前的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's ClassLoader:
// Get the webapp's 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's 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's ClassLoader and may be in use elsewhere
System.out.println("Not deregistering JDBC driver {} as it does not belong to this webapp's 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
}
Run Code Online (Sandbox Code Playgroud)
}
更新:我仍然无法解决此问题.有帮助吗?没有人遇到过这些?
没有解决办法一次性修复所有线程局部泄漏.通常,使用Threadlocal变量的第三方库具有某种清理API调用,可用于清除其本地线程变量.
您必须检查所有报告的threadlocal泄漏,并找到在相应库中处理它们的正确方法.你可以在你的身上做到这一点CustomServletContextListener
例子:
log4j(javadoc):
LogManager.shutdown()
Run Code Online (Sandbox Code Playgroud)
jdbc驱动程序:(javadoc):
DriverManager.deregisterDriver(driver);
Run Code Online (Sandbox Code Playgroud)
注意:还要检查第三方库的新版本,以检查有关内存泄漏(和/或线程本地泄漏)的狐狸修复.
| 归档时间: |
|
| 查看次数: |
2958 次 |
| 最近记录: |