由于Tomcat中的孤立线程,我遇到了内存泄漏。特别是,看起来Guice和JDBC驱动程序并没有关闭线程。
Aug 8, 2012 4:09:19 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [com.google.inject.internal.util.$Finalizer] but has failed to stop it. This is very likely to create a memory leak.
Aug 8, 2012 4:09:19 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak.
我知道这类似于其他问题(例如this one),但在我的情况下,“不用担心它”的答案是不够的,因为它给我带来了问题。我有CI服务器,它会定期更新此应用程序,重新加载6-10次后,CI服务器将挂起,因为Tomcat内存不足。
我需要能够清除这些孤立线程,这样我才能更可靠地运行CI服务器。任何帮助都将不胜感激!
发布于 2013-05-10 01:33:58
我刚刚自己处理了这个问题。与其他一些答案相反,我不建议发出t.stop()
命令。这个方法已经被弃用了,这是有充分理由的。有关此操作的参考Oracle's reasons。
然而,有一种解决方案可以消除此错误,而不需要求助于t.stop()
……
您可以使用@Oso提供的大部分代码,只需替换以下部分
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for(Thread t:threadArray) {
if(t.getName().contains("Abandoned connection cleanup thread")) {
synchronized(t) {
t.stop(); //don't complain, it works
}
}
}
使用MySQL驱动程序提供的以下方法替换它:
try {
AbandonedConnectionCleanupThread.shutdown();
} catch (InterruptedException e) {
logger.warn("SEVERE problem cleaning up: " + e.getMessage());
e.printStackTrace();
}
这应该会正确地关闭线程,并且错误应该会消失。
发布于 2012-09-16 13:16:35
我也遇到过同样的问题,正如Jeff所说,“不用担心”的方法并不可取。
我做了一个在上下文关闭时停止挂起线程的ServletContextListener,然后在web.xml文件上注册了这样的ContextListener。
我已经知道停止线程不是一个优雅的方法来处理它们,但否则服务器在两到三次部署后仍然会崩溃(并不总是可以重启应用服务器)。
我创建的类是:
public class ContextFinalizer implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(ContextFinalizer.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Enumeration<Driver> drivers = DriverManager.getDrivers();
Driver d = null;
while(drivers.hasMoreElements()) {
try {
d = drivers.nextElement();
DriverManager.deregisterDriver(d);
LOGGER.warn(String.format("Driver %s deregistered", d));
} catch (SQLException ex) {
LOGGER.warn(String.format("Error deregistering driver %s", d), ex);
}
}
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for(Thread t:threadArray) {
if(t.getName().contains("Abandoned connection cleanup thread")) {
synchronized(t) {
t.stop(); //don't complain, it works
}
}
}
}
}
创建类后,在web.xml文件中注册它:
<web-app...
<listener>
<listener-class>path.to.ContextFinalizer</listener-class>
</listener>
</web-app>
发布于 2013-07-27 08:06:08
从MySQL连接器5.1.23开始,提供了一种关闭已放弃的连接清理线程AbandonedConnectionCleanupThread.shutdown
的方法。
但是,我们不希望在代码中直接依赖于其他不透明的JDBC驱动程序代码,因此我的解决方案是使用反射来查找类和方法,并在找到时调用它。以下是所需的全部完整代码片段,在加载JDBC驱动程序的类加载器的上下文中执行:
try {
Class<?> cls=Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread");
Method mth=(cls==null ? null : cls.getMethod("shutdown"));
if(mth!=null) { mth.invoke(null); }
}
catch (Throwable thr) {
thr.printStackTrace();
}
如果JDBC驱动程序是MySQL连接器的足够新的版本,这将干净利落地结束线程,否则什么也不做。
注它必须在类加载器的上下文中执行,因为线程是一个静态引用;如果在运行此代码时驱动程序类未被加载或尚未卸载,则线程将不会运行以进行后续的JDBC交互。
https://stackoverflow.com/questions/11872316
复制相似问题