【发布时间】:2015-06-09 15:11:27
【问题描述】:
我已经在 stackoverflow 上搜索并搜索了超过 4 小时的问题的解决方案,阅读并尝试了解正在发生的事情,但我还没有遇到与我的问题相关的解决方案,如果这听起来像一个问题,我深表歉意重复,我会尽力解释到底发生了什么,这样我就可以深入了解 c3p0 的内部工作原理。
我有一个 SpringMVC Web 应用程序在 Tomcat 上运行,并使用 Hibernate、JPA 和 C3P0 作为我的池资源。在页面重新加载时,应用程序对数据库进行几次调用以获取随机图像并将其显示在新页面上。该应用程序在大约 30 次左右的页面重新加载时运行良好,但最终总是崩溃,我必须重新启动 mysql 才能使应用程序再次工作,它给出了以下错误:
2015 年 4 月 4 日下午 12:21:55 com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask 运行 警告:com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@634d8e3d -- 获取尝试失败!!!清除挂起的获取。在尝试获取所需的新资源时,我们未能成功超过允许的最大获取尝试次数 (30)。上次获取尝试异常: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:数据源拒绝建立连接,来自服务器的消息:“连接太多” 在 sun.reflect.GeneratedConstructorAccessor101.newInstance(未知来源) 在 sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 在 java.lang.reflect.Constructor.newInstance(Constructor.java:526) 在 com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 在 com.mysql.jdbc.Util.getInstance(Util.java:386) 在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015) 在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) 在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) 在 com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1114) 在 com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2493) 在 com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2526) 在 com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2311) 在 com.mysql.jdbc.ConnectionImpl.(ConnectionImpl.java:834) 在 com.mysql.jdbc.JDBC4Connection.(JDBC4Connection.java:47) 在 sun.reflect.GeneratedConstructorAccessor43.newInstance(未知来源) 在 sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 在 java.lang.reflect.Constructor.newInstance(Constructor.java:526) 在 com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 在 com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:416) 在 com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:347) 在 com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:135) 在 com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:182) 在 com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171) 在 com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:137) 在 com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014) 在 com.mchange.v2.resourcepool.BasicResourcePool.access$800(BasicResourcePool.java:32) 在 com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810) 在 com.mchange.v2.async.ThreadPerTaskAsynchronousRunner$TaskThread.run(ThreadPerTaskAsynchronousRunner.java:255)
以下是相关文件/配置,以提供问题的上下文:
spring.xml:
<context:component-scan base-package="com.clathrop.infographyl.dao" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="infographylPU" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/infographyl_db" />
<property name="user" value="user" />
<property name="password" value="passwd" />
<!-- Pool properties -->
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="acquireIncrement" value="1" />
<property name="maxStatements" value="0" />
<property name="idleConnectionTestPeriod" value="3000" />
<property name="loginTimeout" value="300" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
persistence.xml:
<persistence-unit name="infographylPU" transaction-type="RESOURCE_LOCAL">
<class>com.clathrop.infographyl.model.Infographic</class>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>
这是一个基本查询的样子,在这个类中,我有一个 EntityManager 作为实例变量,并且在每个查询完成后我关闭() entityManager,但它对数据库中建立的连接数没有影响。
InfographicDaoImpl.java:
@Repository
public class InfographicDaoImpl implements InfographicDao{
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public void insertInfographic(Infographic infographic){
try{
entityManager.persist(infographic);
} catch (Exception e){
e.printStackTrace();
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> findAllInfographics(){
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
List<Infographic> igList = entityManager.createQuery(cq).getResultList();
return igList;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Infographic getRandomInfographic(Integer tableSize){
Random rand = new Random();
int randomIndex = rand.nextInt((tableSize-1)+1) + 1;
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
cq.where(builder.equal(root.<Integer>get("id"), randomIndex));
Infographic randomIg = entityManager.createQuery(cq).getSingleResult();
return randomIg;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Integer getRowCount(){
try{
Number result = (Number) entityManager.createNativeQuery("Select count(id) from infographics").getSingleResult();
return result.intValue();
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> listInfographics(Integer startIndex, Integer pageSize){
List<Infographic> igList = new ArrayList<Infographic>();
String sStartIndex = Integer.toString(startIndex);
String sPageSize = Integer.toString(pageSize);
try{
List list = entityManager.createNativeQuery("Select * from infographics limit " + sStartIndex + ", " + sPageSize).getResultList();
for(Object ig : list){
igList.add((Infographic) ig);
}
return igList;
} catch(Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
}
在这一点上,我对我的应用程序中发生的事情有几个问题。我理解的方式是 c3p0 正在处理与数据库建立的连接,我认为通过池连接,建立的连接将被重用,但是当我查看 mysql 中的进程列表时,我得到的连接列表越来越多。连接数一直在增长,直到应用程序最终抛出上面显示的连接过多警告。我的问题是为什么 c3p0 不重用连接?我是否在某处遗漏了配置来告诉它重用预先存在的连接?为什么 entityManager.close() 似乎没有影响?如果我误解了如何使用 hibernate 和 c3p0,请让我知道我缺少什么。非常感谢任何帮助,在此先感谢。
更新:
严重:Web 应用程序 [/infographyl] 似乎已经启动了一个名为 [com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0] 的线程,但未能停止它。这很可能会造成内存泄漏。
我已将问题范围缩小到当 tomcat 启动并部署相关应用程序时识别出的内存泄漏。我现在的问题是试图找出导致问题的可能配置(或缺少配置)。
我目前的解决方法是在 my.cnf 中设置 wait_timeout 以终止超过 5 秒的进程/连接,目前这似乎没问题,但这不是一个可持续的解决方案,我想知道正确的关闭我的连接的方法。
更新2: com.mchange.* 信息日志:
2015 年 4 月 5 日晚上 10:57:30 org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider 信息:HHH000130:实例化显式连接提供程序:org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider 2015 年 4 月 5 日晚上 10:57:30 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager 信息:初始化 c3p0 池... com.mchange.v2.c3p0.ComboPooledDataSource [acquireIncrement -> 1,acquireRetryAttempts -> 30,acquireRetryDelay -> 1000,autoCommitOnClose -> false,automaticTestTable -> null,breakAfterAcquireFailure -> false,checkoutTimeout - > 0,connectionCustomizerClassName -> null,connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester,dataSourceName -> z8kfsx98137ghpr10fde73|6aa74262,debugUnreturnedConnectionStackTraces -> false,描述 -> null,driverClass -> com.mysql.jdbc.Driver , factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx98137ghpr10fde73|6aa74262, idleConnectionTestPeriod -> 3000, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/infographyl_db, lastAcquisitionFailureDefaultTaskTime -> null, maxAdministrative > 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin - > false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
【问题讨论】:
-
如何获取对象'entityManager'?
-
@SteveWaldman,感谢您的回复。 entityManager 对象是在实例化 InfographicDaoImp 对象时创建的。我将更新代码示例,以便为您提供更好的想法。这是创建/访问 entityManager 的错误方法吗?
-
好的 - 这是否总是发生,或者这是一个生产环境,您发现此类问题偶尔会发生?
-
@clathrop 是的。错误的方法是创建 One 并跨线程使用它,或者在抛出异常等之后仍然使用它。EntityManager.close() 肯定有效。还有其他例外吗?
-
@Antoniossss,这发生在我的本地测试环境中,而且总是这样。我必须重新启动 mysql 才能继续使用该应用程序。
标签: java mysql spring hibernate c3p0