如何避免Tomcat应用程序重新部署时DB2 driver Classloader内存泄漏问题。

4

IBM的广受支持的JDBC驱动程序与Tomcat广受支持的连接池结合使用会导致内存泄漏。 请参考Tomcat应用程序.war文件重新部署时的类加载器内存泄漏。

java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [DB2JccConfiguration.properties]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1327)
    at org.apache.catalina.loader.WebappClassLoaderBase.getResource(WebappClassLoaderBase.java:1023)
    at com.ibm.db2.jcc.am.ud.run(Unknown Source)
    at java.security.AccessController.doPrivileged(AccessController.java:285)
    at com.ibm.db2.jcc.am.GlobalProperties.a(Unknown Source)
    at com.ibm.db2.jcc.am.GlobalProperties.d(Unknown Source)
    at com.ibm.db2.jcc.am.mq.run(Unknown Source)
    at java.util.TimerThread.mainLoop(Timer.java:567)
    at java.util.TimerThread.run(Timer.java:517)

我不理解所建议的解决方案,因为它与将驱动程序JAR包包含在Tomcat lib目录中的最常推荐做法相冲突。
我们需要进行共享部署和重新部署,而无需重新启动Tomcat。如果您有使用此软件组合并遇到此问题的经验,请在此处分享您的解决方案。
3个回答

7
对于驱动程序版本4.22.29,我目前在ServletContextListener中使用以下代码:
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
    // This fixes the JDBC driver not unloading corectly on a context reload  for DB2 JDBC 4.22.29
    try {
        logger.debug("Trying to stop the timer");
        new com.ibm.db2.jcc.am.iq() {
            // instance initializer to execute the fix when the anonymous class is instantiated, i.e. now
            {
                if (a != null) {
                    a.cancel();
                } else {
                    logger.debug("Timer is null, skipped");
                }
            }
        };
        logger.debug("Stopped the timer");
    } catch (Exception e) {
        logger.error("Could not stop the DB2 timer thread", e);
    }
}

注意: 由于DB2驱动程序JAR似乎已经混淆,计时器存储(com.ibm.db2.jcc.am.iq.a)在其他驱动程序版本中可能会有所不同。此外,您正在子类化的类的构造函数可能会产生副作用,在我的情况下没有。

我是如何得到这个解决方案的

获取异常信息

java.lang.NullPointerException
    at org.apache.catalina.loader.WebappClassLoaderBase.getResource(WebappClassLoaderBase.java:1600)
    at com.ibm.db2.jcc.am.wd.run(wd.java:49)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.ibm.db2.jcc.am.GlobalProperties.a(GlobalProperties.java:146)
    at com.ibm.db2.jcc.am.GlobalProperties.d(GlobalProperties.java:100)
    at com.ibm.db2.jcc.am.dr.run(dr.java:124)  <------- point of interest <----------
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)

计时器的主类为com.ibm.db2.jcc.am.dr
使用IntelliJ,在其构造函数中设置断点。等待断点命中:

instantiaed in GlobalProperties

前往实例化的位置,例如在GlobalProperties中。查看它被安排在哪个计时器上。

iq.a.schedule()

找到访问iq.a的方法:由于这是一个静态受保护字段,我们可以从iq继承,并从该类内部访问父类的静态字段来调用a上的cancel()


1

4.19.66版本的修复:

public void contextDestroyed(ServletContextEvent servletContextEvent) {
    // This fixes the JDBC driver not unloading corectly on a context reload  for DB2 JDBC 4.19.66
    try {
        System.out.println("Trying to stop the DB2 timer thread");
        new com.ibm.db2.jcc.am.tp() {
            {
                if (a != null) {
                    a.cancel();
                } else {
                    System.out.println("Timer is null, skipped");
                }
            }
        };
        System.out.println("Stopped the timer");
    } catch (Exception e) {
        System.out.println("Could not stop the DB2 timer thread " + e.getMessage());
    }
}

这个问题对于DB2 v11驱动程序v4.24.92仍然有效吗? - Zilvinas
2
可能不行。我按照phant0m的方法对代码进行了适配,以适应我正在使用的db2版本。您需要为您的版本进行相同的操作。 - Jerebenz

1
这是IBM JDBC驱动程序版本4.19中已确认的错误(无法禁用的计时器任务)。解决方法是降级到版本4.18。

1
我在使用Tomcat和DB2 JDBC驱动程序版本4.22.29时遇到了相同的问题。您在哪里找到确认这是版本4.19的错误? - phant0m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接