問題跟蹤:java
近期在整合SSH(spring、springmvc、hibernate)項目,提供給第三方服務。每當調用內存池達到上限以後,外界調用服務直接失敗,提示[cannot open connection]。通過九牛二虎之力,才找到是c3p0 pool池內存泄漏的緣由。相似jdbc,session使用以後須要手動關閉:程序員
public static Basicinfo getBean(Integer iduser) { Basicinfo u = null; Transaction tx = null; Session sess = NewHibernateUtil.getSessionFactory().openSession(); try { Query q = sess.createQuery("from Basicinfo where iduser=" + iduser); u = (Basicinfo) q.uniqueResult(); if (u == null) { tx = sess.beginTransaction(); //line 69 u = new Basicinfo(); u.setIduser(iduser); tx.commit(); } } catch (Exception ex) { ex.printStackTrace(); if(tx != null) { try { tx.rollback(); } catch(Exception e){e.printStackTrace;} } } finally { if(sess!=null) { sess.close(); } } return u; }
SSM框架中spring進行的事務管理,事務返回以後會自動關閉session。此時得到session的方式包含三種:web
一、getCurrentSession()
得到當前會話中的session,該session有容器自行維護管理,spring能夠代理事務。spring
二、this.getSession()
從當前的執行中得到或建立一個hibernate的session對象,本身關閉,釋放鏈接資源,spring能夠代理事務。數據庫
三、openSession()
調用函數自行建立一個數據庫的鏈接,並將其打開,在使用Spring操做非查詢語句的請況下,Spring的事務對該session對象不起到事務管理的做用,因此該session對象應當由程序員本身關閉,釋放鏈接資源。express
應用程序中dao層使用spring的HibernateDaoSupport模板,經過spring來管理session生命週期,則首選getCurrentSession() ;apache
跟蹤 c3p0 pool池鏈接:api
<property name="maxIdleTime">
<!--最大空閒時間,超過空閒時間未使用則鏈接被丟棄。若爲0則永不丟棄。Default: 0 -->
<value>30</value>
</property>
<property name="unreturnedConnectionTimeout">
<!-- 自動超時回收Connection 單位秒 -->
<value>40</value>
</property>
<property name="debugUnreturnedConnectionStackTraces">
<value>true</value>
</property>
參數一:maxIdleTime 最大空閒時間,超過空閒時間未使用則鏈接被丟棄。若爲0則永不丟棄。Default: 0 。tomcat
參數二:debugUnreturnedConnectionStackTraces 參數確定是設置爲true的,打印堆棧跟蹤信息。session
參數三:unreturnedConnectionTimeout 在鏈接被應用程序 checkout後指定時間內未checkin則由鏈接緩衝池執行kill操做。maxIdleTime設置30秒,unreturnedConnectionTimeout設置40秒,達到最大存活,連接不能被緩衝池正常關閉,則出現泄漏。此時,再過10秒後鏈接緩衝池主動執行kill。
debugUnreturnedConnectionStackTraces 和 unreturnedConnectionTimeout 詳細介紹 http://www.mchange.com/projects/c3p0/#configuring_to_debug_and_workaround_broken_clients
打印信息:
09:22:17,909 INFO [com.mchange.v2.resourcepool.BasicResourcePool] (Timer-1) A checked-out resource is overdue, and will be destroyed: com.mchange.v2.c3p0.impl.NewPooledConnection@1e029db 09:22:17,911 INFO [com.mchange.v2.resourcepool.BasicResourcePool] (Timer-1) Logging the stack trace by which the overdue resource was checked-out.: java.lang.Exception: DEBUG ONLY: Overdue resource check-out stack trace. at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:506) [c3p0-0.9.1.2.jar:0.9.1.2] at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525) [c3p0-0.9.1.2.jar:0.9.1.2] at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128) [c3p0-0.9.1.2.jar:0.9.1.2] at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:83) [spring-orm-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446) [hibernate3.jar:] at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167) [hibernate3.jar:] at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:161) [hibernate3.jar:] at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1573) [hibernate3.jar:] at org.hibernate.loader.Loader.doQuery(Loader.java:696) [hibernate3.jar:] at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) [hibernate3.jar:] at org.hibernate.loader.Loader.doList(Loader.java:2228) [hibernate3.jar:] at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125) [hibernate3.jar:] at org.hibernate.loader.Loader.list(Loader.java:2120) [hibernate3.jar:] at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:312) [hibernate3.jar:] at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1722) [hibernate3.jar:] at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165) [hibernate3.jar:] at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:175) [hibernate3.jar:] at com.pec.dao.impl.ServiceIntTypeDaoImpl.getIntTypes(ServiceIntTypeDaoImpl.java:45) [classes:] at com.pec.dao.impl.CacheDaoImpl.uploadInterfaceCache(CacheDaoImpl.java:80) [classes:] at com.pec.service.impl.CacheServiceImpl.uploadInterfaceCache(CacheServiceImpl.java:135) [classes:] at com.pec.service.impl.CacheServiceImpl$$FastClassBySpringCGLIB$$53861b3b.invoke(<generated>) [spring-core-3.2.8.RELEASE.jar:] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) [spring-core-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) [spring-tx-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) [spring-tx-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) [spring-tx-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633) [spring-aop-3.2.8.RELEASE.jar:3.2.8.RELEASE] at com.pec.service.impl.CacheServiceImpl$$EnhancerBySpringCGLIB$$4db83b52.uploadInterfaceCache(<generated>) [spring-core-3.2.8.RELEASE.jar:] at com.pec.controller.PecInterfaceController.pecInterface(PecInterfaceController.java:138) [classes:] at sun.reflect.GeneratedMethodAccessor60.invoke(Unknown Source) [:1.7.0_25] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_25] at java.lang.reflect.Method.invoke(Method.java:606) [rt.jar:1.7.0_25] at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215) [spring-web-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) [spring-web-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:855) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:754) [jboss-servlet-api_3.0_spec-1.0.0.Final.jar:1.0.0.Final] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829) [spring-webmvc-3.2.8.RELEASE.jar:3.2.8.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.0.Final.jar:1.0.0.Final] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) [spring-web-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-3.2.8.RELEASE.jar:3.2.8.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:] at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:] at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:518) [jbossweb-7.0.13.Final.jar:] at org.jboss.threads.SimpleDirectExecutor.execute(SimpleDirectExecutor.java:33) at org.jboss.threads.QueueExecutor.runTask(QueueExecutor.java:801) at org.jboss.threads.QueueExecutor.access$100(QueueExecutor.java:45) at org.jboss.threads.QueueExecutor$Worker.run(QueueExecutor.java:842) at java.lang.Thread.run(Thread.java:724) [rt.jar:1.7.0_25] at org.jboss.threads.JBossThread.run(JBossThread.java:122)
核心問題發現dao層使用openSession()得到session而沒有進行手動關閉:
@Override public List<String> getIntTypes(JSONObject js) { // 建立相關SQL語句 StringBuffer sb = new StringBuffer(); sb.append("select "); sb.append(" int_type "); sb.append(" from "); sb.append(" interface_admin.interface_service_int_type"); sb.append(" where "); sb.append(" service_id ='" + js.getString("p_service_id")+ "'"); sb.append(" order by int_type_level asc"); SQLQuery q = this.getSessionFactory().openSession().createSQLQuery(sb.toString()) ; /** 推薦使用 * SQLQuery q = this.getSessionFactory().getCurrentSession().createSQLQuery(sb.toString()) ; * SQLQuery q = this.getSession().createSQLQuery(sb.toString()) ; * */ if ( !CollectionUtils.isNullOrEmpty(q) ) { @SuppressWarnings("unchecked") List<String> beanList = q.list(); return beanList; } return null; }
項目結構:
spring事務管理配置:
<!--五、Spring 配置聲明式事物 --> <!-- 配置事務 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 配置事務範圍 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="false" propagation="NOT_SUPPORTED" /> <tx:method name="find*" read-only="false" propagation="NOT_SUPPORTED" /> <tx:method name="download*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="upload*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="anscy*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 定義切面 --> <!-- <aop:config>裏面有一個"proxy-target-class"屬性,true,那麼基於類的代理將起做用;false或者這個屬性被省略,那麼基於接口的代理將起做用 --> <aop:config proxy-target-class="true"> <aop:pointcut id="pointcut" expression="execution(* com.pec.service.impl.*Impl.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /> </aop:config>
此時spring事務管理範圍是: com.pec.service.impl包下,以Impl結尾的類下全部方法。事務返回以後,spring會對鏈接進行管理。