简体   繁体   English

如何避免DB2驱动程序Classloader内存泄漏Tomcat应用程序.war文件重新部署

[英]How to avoid DB2 driver Classloader Memory leak on Tomcat application .war file redeployment

IBM's well supported JDBC driver creates a memory leak in combination with Tomcat's well supported connection pool. IBM受到良好支持的JDBC驱动程序与Tomcat支持良好的连接池一起创建了内存泄漏。 Please refer to Classloader memory leak on Tomcat application .war file redeployment. 请参阅Tomcat应用程序.war文件重新部署中的Classloader内存泄漏。

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)

I do not understand the suggested solution as it is in conflict with the most recommended practice of including the driver jar in the Tomcat lib directory. 我不理解建议的解决方案,因为它与在Tomcat lib目录中包含驱动程序jar的最推荐做法相冲突。

We need shared deployment and re-deployment without Tomcat re-start. 我们需要共享部署和重新部署,而无需重新启动Tomcat。 Please share your solution here if you have experience with this software combination and the described issue. 如果您有使用此软件组合和所述问题的经验,请在此处分享您的解决方案。

For driver version 4.22.29, I'm currently using this code in a ServletContextListener: 对于驱动程序版本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);
    }
}

Note : Since the DB2 driver JAR appears to be obfuscated, the timer storage ( com.ibm.db2.jcc.am.iq.a ) will probably be different for other driver versions. 注意 :由于DB2驱动程序JAR似乎被混淆,因此对于其他驱动程序版本,计时器存储( com.ibm.db2.jcc.am.iq.a )可能会有所不同。 Also, the constructor of the class you're subclassing might have side effects, in my case there are none. 此外,您子类化的类的构造函数可能有副作用,在我的情况下没有。

How I got to this solution 我是如何得到这个解决方案的

Get the exception 得到例外

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)

The main class for the timer is com.ibm.db2.jcc.am.dr . 计时器的主要类是com.ibm.db2.jcc.am.dr

Using IntelliJ, I set a breakpoint in its constructor. 使用IntelliJ,我在其构造函数中设置了一个断点。 Wait for the breakpoint to hit: 等待断点命中:

在GlobalProperties中实例化

Go to where it's instantiated, in my case in GlobalProperties. 转到它实例化的地方,在我的例子中是GlobalProperties。 Look on what timer it is scheduled. 看看它的计划时间。

iq.a.schedule()

Find a way to access iq.a : Since this is a static protected field, we can inherit from iq and from inside that class, access the static field of the parent class to call cancel() on a . 找到一种访问iq.a :由于这是一个静态受保护的字段,我们可以从iq继承并从该类内部访问父类的静态字段以在a上调用cancel()

This is a confirmed bug in the IBM JDBC driver version 4.19 (timer task that cannot be disabled). 这是IBM JDBC驱动程序版本4.19(无法禁用的计时器任务)中的已确认错误。 The workaround is to downgrade to version 4.18. 解决方法是降级到4.18版。

Fix for version 4.19.66: 修复版本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());
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM