Tomcat Guice/JDBC Memory Leak

2022-08-31 20:51:13

由于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.

我知道这与其他问题(例如这个问题)类似,但在我的情况下,“不要担心它”的答案是不够的,因为它给我带来了问题。我有CI服务器定期更新此应用程序,在6-10次重新加载后,CI服务器将挂起,因为Tomcat内存不足。

我需要能够清除这些孤立的线程,以便我可以更可靠地运行我的CI服务器。任何帮助将不胜感激!


答案 1

我自己刚刚处理了这个问题。与其他一些答案相反,我不建议发出命令。此方法已被弃用,并且有充分的理由。参考 Oracle 这样做的原因t.stop()

但是,有一种解决方案可以消除此错误,而无需诉诸...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();
}

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


答案 2

我也遇到过同样的问题,正如杰夫所说,“不要担心它的方法”不是要走的路。

我做了一个ServletContextListener,当上下文关闭时停止挂起的线程,然后在web上注册这样的IntextListener.xml文件。

我已经知道停止线程并不是处理它们的优雅方法,但除此之外,服务器在两到三次部署后会继续崩溃(并不总是能够重新启动应用程序服务器)。

我创建的类是:

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>

推荐