在Tomcat中启用Context reload =“true”时,JDBC连接池用完了连接

我正在Eclipse Juno中开发Java EE Web应用程序。 我已将Tomcat配置为使用JDBC连接池(org.apache.tomcat.jdbc.pool)以及PostgreSQL数据库。 以下是我的项目的META-INF / context.xml中的配置:

     

我的应用程序使用Eclipse部署到Tomcat,在Tomcat的context.xml中,属性reloadable设置为“true”,以便在检测到更改时自动重新加载Web应用程序:

我注意到,每次上面提到的自动重新加载时,都会保留10个与PostgreSQL db的连接(因为在webapp的context.xml中,initialSize =“10”)。 因此,在10次更改后抛出PSQLException:

 org.postgresql.util.PSQLException: FATAL: sorry, too many clients already ... 

如果我手动重启Tomcat – 一切都很好,只保留了10个连接。

有没有人知道解决这个问题的方法,所以有可能将reloadable设置为“true”并且每次重新加载上下文时都不会导致更多连接汇集?

非常感谢任何帮助。

PS Apache Tomcat版本7.0.32

解决方案(tl; dr)

为了解决此问题,请在context.xml文件中添加一个属性closeMethod ( 此处记录 ),其值为“ close ”。

这是我的/META-INF/context.xml文件的正确内容:

     

注意属性closeMethod 。 我测试了它,现在连接数保持严格,如context.xml文件中所定义!

注意
有一个时刻(与JNDI有关)可以照顾。 有关完整说明,请参阅更新3。


答案很长

好的,我发现上述解决方案归功于Apache Tomcat委托人Konstantin Kolinko 。 我在ASF Bugzilla上将此问题报告为Apache Tomcat错误, 结果certificate这不是错误 (请参阅更新1)。

=== 更新1(2012-12-03)又名“新希望” ===

好吧,它仍然是一个错误 。 Apache Tomcat 7发布经理Mark Thomas 确认 (引用):

“这是jdbc-pool中的内存泄漏错误.PoolCleaner实例保留对ConnectionPool的引用,以防止它被GC。

这已在trunk和7.0.x中修复,并将包含在7.0.34之后。“

因此,如果您有较旧的Tomcat版本(小于7.0.34),请使用上述解决方案,否则, 从Apache Tomcat版本7.0.34开始,应该没有像我描述的那样的问题。 (见更新2)

=== 更新2(2014-01-13)又名“问题反击” ===

看起来我的错误报告中最初描述的问题仍然存在,即使对于当前最新的Apache Tomcat版本7.0.50,我也用Tomcat 7.0.47复制它(感谢Miklos Krivan指出它)。 虽然现在Tomcat有时会在重新加载后设法关闭其他连接,但有时连接数会在重新加载后增加然后保持稳定,但最终这种行为仍然不可靠。

我仍然可以重现最初描述的问题(尽管不是那么容易:它可能与连续重载的频率有关)。 看起来这只是时间问题,即如果Tomcat在重新加载后有足够的时间,它会或多或少地管理连接池。 正如Mark Thomas在他的评论中所提到的那样:“根据closeMethod的文档,该方法仅用于加速资源的释放,否则这些资源将由GC释放。” (引用结束),看起来速度是决定因素。

当使用Konstantin Kolinko提供的解决方案(使用closeMethod =“close”)时,一切正常,并且保留的连接数保持严格,如context.xml文件中所定义。 所以看起来使用closeMethod =“close”是唯一真正的方式(目前),以避免在上下文重新加载后耗尽连接。

=== 更新3(2014-01-13)又名“Tomcat发布经理的回归” ===

解决了UPDATE 2中描述的行为背后的奥秘。 在我收到Mark Thomas(Tomcat发布经理)的回复后,现在已经清除了更多细节。 我希望这是最后一次更新。 所以这个错误确实已经在UPDATE 1中提到了。我在Mark的回复中发布了重要的部分作为引用(强调我的):

根据评论#4到#6,在调查此错误时发现的实际内存泄漏已在7.0.34开始修复。

连接未在重新加载时关闭的问题是JNDI资源的J2EE规范的结果,因此这部分错误报告无效。 我正在恢复此错误的状态以修复以反映确实存在的内存泄漏已得到修复。

为了扩展重新加载后无法立即关闭连接的原因无效,J2EE规范没有为容器提供告知资源不再需要的机制。 因此,所有容器都可以做的是对资源的明确引用并等待垃圾收集(这将触发池​​和关联连接的关闭)。 垃圾收集有时会由JVM确定,因此这就是为什么在上下文重新加载后关闭连接需要不确定的时间,因为垃圾收集可能在一段时间内不会发生。

Tomcat添加了Tomcat特定的JNDI属性closeMethod,该属性可用于在上下文停止时触发JNDI资源的显式关闭。 如果等待GC清理资源是不可接受的,那么只需使用此参数即可 。 Tomcat默认不使用它,因为它可能会对某些JNDI资源产生意外和不需要的副作用

如果您希望看到一个标准机制来告诉JNDI资源它们不再需要它们,那么您需要游说J2EE专家组。

结论

只需使用本文开头介绍的解决方案(但是,以防万一,请记住理论上可能因使用它而产生的JNDI相关问题)。


替代解决方案

Michael Osipov建议使用他的CloseableResourceListener ,它可以防止在取消部署Web应用程序时因左开放资源而导致内存泄漏。 所以你也可以尝试一下。


免责声明
UPDATES的别名受到星球大战电影系列的启发。 所有权利属于其各自所有者。