首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Tomcat Guice/JDBC内存泄漏

Tomcat Guice/JDBC内存泄漏
EN

Stack Overflow用户
提问于 2012-08-09 04:05:13
回答 5查看 52.2K关注 0票数 50

由于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服务器。任何帮助都将不胜感激!

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 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();
}

这应该会正确地关闭线程,并且错误应该会消失。

票数 52
EN

Stack Overflow用户

发布于 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>
票数 15
EN

Stack Overflow用户

发布于 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交互。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11872316

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档