我正在Eclipse Juno中开发一个Java EE Web应用程序。我已经将Tomcat配置为使用JDBC连接池(org.apache.tomcat.jdbc.pool)沿着PostgreSQL数据库。以下是我的项目的META-INF/context.xml中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Configuration for the Tomcat JDBC Connection Pool -->
<Resource name="jdbc/someDB"
type="javax.sql.DataSource"
auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/somedb"
username="postgres"
password="12345"
maxActive="100"
minIdle="10"
initialSize="10"
validationQuery="SELECT 1"
validationInterval="30000"
removeAbandoned="true"
removeAbandonedTimeout="60"
abandonWhenPercentageFull="50" />
</Context>
我的应用程序是使用Eclipse部署到Tomcat的,在Tomcat的context.xml中,一个属性reloadable被设置为“true”,以便在检测到更改时自动重新加载Web应用程序:<Context reloadable="true">
我注意到每次上述的自动重新加载发生时,会保留10个到PostgreSQL数据库的连接(因为在webapp的context.xml initialSize=“10”中)。因此,在10次更改后,会抛出一个PSQLException:
org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
...
如果我手动重新启动Tomcat -一切正常,只保留了10个连接。
有没有人知道解决这个问题的方法,这样就有可能在开发时将reloadable设置为“true”,而不会在每次重新加载上下文时导致更多的连接池?
会很感激你的帮助。
附言:Apache Tomcat 7.0.32版
1条答案
按热度按时间62o28rlo1#
解决方案(tl;驱动器)
为了解决此问题,请将值为“close”的属性
closeMethod
(在此处记录)添加到context.xml文件中的 Resource 元素。下面是我的/META-INF/context. xml文件的正确内容:
注意属性 closeMethod。我测试了它,现在连接数严格按照 context.xml 文件中的定义保持!
注意事项
有一个时刻(与JNDI相关)可能需要注意。请参见UPDATE 3以获得完整的描述。
长长的回答
好的,我找到了上面的解决方案,感谢Apache Tomcat的提交者Konstantin Kolinko。我报告this issue是ASF Bugzilla上的一个Apache Tomcat bug,结果证明它不是bug(请参阅UPDATE 1)。
更新1(2012年12月3日)又名“新希望”
好吧,它仍然被证明是一个bug。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版开始,应该不会出现我所描述的问题。(请参阅UPDATE 2)
==更新2(2014年1月13日),又名“问题反击”===
看起来my bug report中最初描述的问题仍然存在,甚至对于当前最新的Apache Tomcat版本7.0.50也是如此,我还在Tomcat 7.0.47中重现了它(感谢Miklos Krivan指出)虽然现在Tomcat有时会在重新加载后设法关闭额外的连接,有时连接数在一次重新加载后增加,然后保持稳定,但是最终这种行为仍然是不可靠的。
我仍然可以重现最初描述的问题(尽管也不是那么容易:这可能与连续重新加载的频率有关)。看起来这只是时间问题,也就是说,如果Tomcat在重新加载后有足够的时间,它就会或多或少地管理连接池,就像Mark托马斯在他的评论(引用)中提到的那样:“根据closeMethod的文档,该方法的存在只是为了加速释放资源,否则这些资源将由GC释放。”(引号结束),看起来速度是决定性因素。
当使用Konstantin Kolinko提出的解决方案(使用
closeMethod="close"
)时,一切都很好,并且保留的连接数严格按照context.xml文件中的定义保持。因此,使用closeMethod="close"
似乎是(目前)避免在上下文重载后耗尽连接的唯一真正方法。==更新3(2014-01-13),又名“Tomcat发布管理器的回归”===
UPDATE 2中描述的行为背后的谜团被解开了。在我收到Mark托马斯(Tomcat发布经理)的回复后,更多的细节已经被清除。我希望这是最后一次更新。所以bug确实被修复了,正如UPDATE 1中提到的那样。我在这里引用Mark回复中的关键部分(强调我的):
根据注解#4到#6,在调查此错误时发现的实际内存泄漏已在7.0.34及更高版本中得到修复。
重新加载时未关闭连接的问题是JNDI资源的J2EE规范的结果,因此错误报告的这一部分无效。我将此错误的状态恢复为已修复,以反映确实存在的内存泄漏已得到修复。
要详细说明为什么在重新加载后无法立即关闭连接是无效的,J2EE规范没有为容器提供告诉资源不再需要它的机制,因此容器所能做的就是清除对资源的引用并等待垃圾收集(这将触发池和关联连接的关闭)。垃圾收集在JVM确定的时间发生,因此这就是为什么在上下文重新加载之后关闭连接需要不确定的时间量的原因,因为垃圾收集可能在一段时间内不发生。
Tomcat添加了Tomcat特定的JNDI属性 closeMethod,该属性可用于在上下文停止时触发JNDI资源的显式关闭。如果等待GC清理资源是不可接受的,则只需使用此参数。Tomcat默认情况下不使用此参数,因为它可能会对某些JNDI资源产生意外和不希望的副作用**。
如果您希望看到提供一种标准机制来告诉JNDI资源不再需要它们,那么您需要游说J2EEMaven组。
结论
只需使用本文开头介绍的解决方案(但是,为了以防万一,请记住使用它时可能会 * 理论上 * 出现的JNDI相关问题)。
替代解决方案
Michael Osipov建议使用他的 * ClosebleResourceListener *,它可以防止在取消部署web应用程序时由于留下打开的资源而导致的内存泄漏,所以你也可以给予。
更新的别名灵感来自于Star Wars系列电影。所有权利归各自所有者所有。*