服务器闲置一夜后的 C3P0 死锁

Posted

技术标签:

【中文标题】服务器闲置一夜后的 C3P0 死锁【英文标题】:C3P0 Deadlock after server sits idle over night 【发布时间】:2014-03-19 17:06:23 【问题描述】:

我有一个在 tomcat 上运行的 java servlet 应用程序,并在 tomcat 中配置了 c3p0。我有一个应用程序,在我的组织中只有少数人在内部使用。通常有 3 个或更少的人使用它,有时最多 5 个。所以,不是很多人。通常,并且不可预测地,应用程序失败并出现以下错误。通常它发生在长时间的空闲时间(一夜之间)之后,并在第二天的第一个请求上失败。我有其他具有类似堆栈的生产应用程序没有这个问题,但它们很少空闲。这是错误(是的,我搜索了 *** 并看到其他有类似问题的人,但似乎没有一个解决方案适合我):

WARNING: com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@2a3252ed -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!
Mar 19, 2014 6:42:11 AM com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector run
WARNING: com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@2a3252ed -- APPARENT DEADLOCK!!! Complete Status:
        Managed Threads: 3
        Active Threads: 3
        Active Tasks:
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@b7eed31
                        on thread: C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#2
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@3a0631dc
                        on thread: C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#1
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@6cb38046
                        on thread: C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#0
        Pending Tasks:
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@7caaab6a
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@28dbfc17
                com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@2835ef6
                com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@7ecf69c3
                com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@6328c23f
                com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@8e8e8f5
                com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@73163164
                com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask@3604d743
                com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask@77d08b69
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@1c70d7f3
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@3471a11b
Pool thread stack traces:
        Thread[C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#2,5,main]
                java.net.SocketInputStream.socketRead0(Native Method)
                java.net.SocketInputStream.read(SocketInputStream.java:146)
                com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
                com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
                com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
                com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3116)
                com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3573)
                com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3562)
                com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4113)
                com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
                com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
                com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2812)
                com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2761)
                com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1612)
                com.mysql.jdbc.ConnectionImpl.getTransactionIsolation(ConnectionImpl.java:3352)
                com.mchange.v2.c3p0.impl.NewPooledConnection.<init>(NewPooledConnection.java:125)
                com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:211)
                com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:184)
                com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:200)
                com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1086)
                com.mchange.v2.resourcepool.BasicResourcePool.doAcquireAndDecrementPendingAcquiresWithinLockOnSuccess(BasicResourcePool.java:1073)
                com.mchange.v2.resourcepool.BasicResourcePool.access$800(BasicResourcePool.java:44)
                com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask.run(BasicResourcePool.java:1810)
                com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:648)
        Thread[C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#1,5,main]
                com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:672)
        Thread[C3P0PooledConnectionPoolManager[identityToken->2ykl7d901mnvek411mheiv|3a7f5639]-HelperThread-#0,5,main]
                com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:672)

这是我在 tomcat 中的配置。 “大写锁定”中的项目已更改以保护无辜者:

<Resource name="jdbc/NAME_HERE" auth="Container"
              description="DESCRIPTION"
              jdbcUrl="jdbc:mysql://PATH_TO_RDS?autoReconnect=true"
              user="USER"
              password="PASS"
              type="com.mchange.v2.c3p0.ComboPooledDataSource"
              factory="org.apache.naming.factory.BeanFactory"
              driverClass="com.mysql.jdbc.Driver"
              maxPoolSize="50"
              minPoolSize="10"
              acquireIncrement="5"
              acquireRetryAttempts="0"
              acquireRetryDelay="3000"
              breakAfterAcquireFailure="false"
              maxConnectionAge="60"
              maxIdleTime="30"
              maxIdleTimeExcessConnections="10"
              idleConnectionTestPeriod="15"
              testConnectionOnCheckout="true"
              preferredTestQuery="SELECT 1"
              debugUnreturnedConnectionStackTraces="true"
              autoCommitOnClose="true"
    />

我在服务器上使用 C3P0 0.9.2.1、mchange 0.2.3.4 和 mysql 连接器 5.1.26。我也在应用程序中使用hibernate 3.2.5.ga。

我不知道这是否重要,但我们使用亚马逊的 RDS,所以它不在本地主机上。

任何帮助将不胜感激。这已经持续了一段时间,我尝试了各种论坛上能找到的所有东西,但都没有运气。

【问题讨论】:

那时我会尝试不同的连接池。 Tomcat 连接池是一个不错的现代替代方案people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html 像这样的调试问题非常复杂,所以我怀疑你能从 *** 得到答案。但你永远不知道 ;) 编辑:这是 Tomcat 池文档tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html 的更好链接 嗯。所以这个有一些不寻常的地方。仅通过活动任务部分,所有 ScatteredAcquireTasks,我通常会说“c3p0 的线程池线程在连接获取中挂起。尝试从 DBMS 获取连接被挂起,既没有成功也没有失败并出现异常。这就是问题所在你需要调试。”但是,查看活动的线程堆栈跟踪,这还不是全部。一个线程在连接获取时挂起,另一个在试图获取线程池的监视器时挂起,第三个原因仍然是神秘的。 autoReconnect=true 对我来说看起来很可疑。在连接池环境中,池而不是驱动程序更好地处理重新连接。如果您删除该标志会发生什么?有关 MySQL 文档 ***.com/questions/667289/… 的相关引用,请参阅以下帖子 【参考方案1】:

只是为了提供一个答案,这一切都被证明是一个 JVM 内存问题。 JVM 会在一夜之间耗尽内存。我不知道为什么,但是这台服务器为 tomcat 打开了一些特殊的垃圾收集器选项,当我删除这些选项时,不仅问题消失了,而且该机器上的内存占用也小了很多。默认垃圾收集器必须比正在使用的垃圾收集器更具侵略性。这个问题已经好几个星期没有发生了,也从来没有与 c3p0 相关,我不认为。 C3P0 只是发现和报告问题的一个。

【讨论】:

介意分享您使用的 GC 标志吗?我现在正遇到这个确切的问题,很想试试你的设置。谢谢 好吧,我删除了坏标志,所以我不是 100% 确定,但我认为我使用的是 -XX:+UseConcMarkSweepGC。我当前的设置是使用:JAVA_OPTS="-Djava.awt.headless=true -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/share/tomcat7/dumps"。请注意,当它再次崩溃时,最后两个被放入以捕获堆转储,但由于删除上述选项修复了它,我从来没有处理过这些。

以上是关于服务器闲置一夜后的 C3P0 死锁的主要内容,如果未能解决你的问题,请参考以下文章

当线程全部为空时,C3P0 明显死锁?

Java Spring 的 C3P0 死锁

c3p0数据库连接池死锁问题

连接池详解,c3p0与dbcp的区别!

Hibernate、C3P0、Mysql——断管

小伙挖矿一夜暴富,背后的真相究竟是什么?