Tomcat Guice/JDBC 内存泄漏

13 浏览
0 Comments

Tomcat Guice/JDBC 内存泄漏

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

admin 更改状态以发布 2023年5月22日
0
0 Comments

我曾经遇到了同样的问题,正如 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 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 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 文件中:


        path.to.ContextFinalizer
    

0
0 Comments

我刚刚自己解决了这个问题。与其他答案不同,我不建议发出t.stop()命令。这种方法已经过时,有充分的理由。参考Oracle的原因

但是有一种方法可以在不需要使用t.stop()的情况下解决此错误...

您可以使用@Oso提供的大部分代码,只需更换以下部分

Set 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();
}

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

0