spring声明式事务无法关闭session

10-11-24 tianyarider
              

手上一个问题困扰我好长时间了,请banq老师抽空支个招啊。

目前有一已上线试运行项目,Spring+Struts2+Hibernate,外加Quartz框架跑定时任务,Hibernate lazy="true",自然用上了openSessionInViewFilter,Ehcache二级缓存仅缓存只读的一小部分数据,应用服务器采用Tomcat6,物理服务器Linux+4G RAM,server.xml配置如下:

// <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
		maxThreads="300" minSpareThreads="50" maxIdleTime="60000"/>
    <Connector executor="tomcatThreadPool"
               port="80" protocol="HTTP/1.1" 
               connectionTimeout="20000" enableLookups="false"
               redirectPort="8443" URIEncoding="UTF-8" acceptCount="50"
	       compression="on"
	       compressionMinSize="2048"
	       compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
	       disableUploadTimeout="true"/>
<p>

JVM配置参数如下:

// JAVA_OPTS="-server -Xms2048m -Xmx2048m -Xmn512m -XX:PermSize=64m -XX:MaxPermSize=128m -XX:MaxNewSize=256m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:-UseGCOverheadLimit -XX:+HeapDumpOnOutOfMemoryError -Djava.awt.headless=true"
<p>

现在情况是,JVM内存最大配了2G,查看tomcat list applications 默认session有效时间(30分)内也就30-50个session,server status 过半天,free memory就剩100多M了,甚至就个位数了,GC也仅能回收几十M,必须重启,jmap取得堆转储文件,用Eclipse Memoery Analyzer分析,内存泄漏,其中80%多是被连接占用,如下:

89 instances of "org.hibernate.impl.SessionFactoryImpl", loaded by "org.apache.catalina.loader.WebappClassLoader @ 0x8bead108" occupy 854,135,176 (82.98%) bytes.

These instances are referenced from one instance of "java.util.HashMap$Entry[]", loaded by "<system class loader>"

// Class Name Ref. Objects Shallow Heap Ref. Shallow Heap Retained Heap 
org.quartz.simpl.SimpleThreadPool$WorkerThread @ 0xb65524f8 scheduler_Worker-7 Busy Monitor, Thread 89 120 11,392 392 
contextClassLoader org.apache.catalina.loader.WebappClassLoader @ 0x8bead108 89 168 11,392 5,814,616 
classes java.util.Vector @ 0x8ca08598 89 24 11,392 20,520 
elementData java.lang.Object[5120] @ 0x8cd19528 89 20,496 11,392 20,496 
<p>[2268] class org.hibernate.impl.SessionFactoryObjectFactory @ 0xed5dbd68 89 24 11,392 2,896 
INSTANCES org.hibernate.util.FastHashMap @ 0x8c904918 89 16 11,392 2,720 
map java.util.HashMap @ 0xc8f562a8 89 40 11,392 2,704 
table java.util.HashMap$Entry[128] @ 0xc8f562d0 89 528 11,392 2,664 
<p>[17] java.util.HashMap$Entry @ 0xc8f565a0 » 4 24 512 96 
<p>[55] java.util.HashMap$Entry @ 0xc8f567b0 » 4 24 512 96 
<p>[109] java.util.HashMap$Entry @ 0xc8f56a98 » 4 24 512 96 
<p>[107] java.util.HashMap$Entry @ 0xc8f56a80 » 3 24 384 72 
<p>[0] java.util.HashMap$Entry @ 0xc8f564e0 » 2 24 256 48 
......
<p>[25] java.util.HashMap$Entry @ 0xc8f565e8 » 1 24 128 24 
Total: 25 of 66 entries 89 1,584 11,392 
<p>

好像Quartz的嫌疑最大,在proxool管理页面,可以看到我初始连接开了12个,最大连接数为128个,当其中一个quartz任务线程启动,而事实上任务本身只是发了一条语句而已,活动连接数居然一下子就到68个了,关键是任务都是受控于Spring声明式事务内的啊,不需要我们显式的开关吧,吐血了,下面再附上applicationContext.xml:

// <bean id="DataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
        <property name="driver" value="oracle.jdbc.driver.OracleDriver"></property>
        <property name="driverUrl" value="jdbc:oracle:thin:@172.168.1.144:1521:orcl10G"></property>
        <property name="user" value="aaa"></property>
        <property name="password" value="xsgldb"></property>
        <property name="alias" value="xsgldb_pool" />
    	<property name="prototypeCount" value="4" /> 
    	<property name="maximumConnectionCount" value="128" /> 
    	<property name="minimumConnectionCount" value="12" /> 
    	<property name="simultaneousBuildThrottle" value="128" /> 
    	<property name="maximumActiveTime" value="300000" />
    	<property name="houseKeepingSleepTime" value="99000" />
    	<property name="houseKeepingTestSql" value="select 1 from dual" />  
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<property name="dataSource">
        	<ref bean="DataSource" />
        </property>
            <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                <prop key="hibernate.show_sql">false</prop>
                
                <!-- 指定Hibernate持久化操作的批操作大小 -->
                <prop key="hibernate.jdbc.batch_size">30</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.max_fetch_depth">2</prop>
                <prop key="hibernate.default_batch_fetch_size">10</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
                <prop key="hibernate.connection.release_mode">auto</prop>
                <prop key="hibernate.transaction.flush_before_completion">true</prop>
                <prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory </prop>
            </props>
        </property>
        <property name="mappingDirectoryLocations">
     		<list>
     			<value>classpath:/cn/bbb/aaa/entity</value>
     		</list>
     	</property>
	</bean>
	<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="find*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="pagedQuery*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="merge*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <aop:pointcut id="interceptorPointCuts"
            expression="execution(* cn.bbb.aaa.*.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice"
            pointcut-ref="interceptorPointCuts" />        
    </aop:config> 
	<bean id="simpleThreadPoolTaskExecutor" 
			class="org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor">
		<property name="makeThreadsDaemons" value="true"/>
		<property name="threadCount" value="3" />
		<property name="threadPriority" value="5" />
		<property name="threadsInheritContextClassLoaderOfInitializingThread" value="true"/>
		<property name="threadNamePrefix" value="quartz-simpleThreadPoolTaskExecutor"/>
		<property name="waitForJobsToCompleteOnShutdown" value="true" />
	</bean> 
	<bean id="scheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="taskExecutor" ref="simpleThreadPoolTaskExecutor"/>
		<property name="triggers">
			<list>
				<ref bean="expiredPrepareTrigger"/>
			</list>
		</property>
	</bean>
<p>

[该贴被tianyarider于2010-11-24 13:57修改过]

              

tianyarider
2010-11-24 17:02

高人可否指点一下,我的这个问题是否是classloader leak,详情见附件,由Eclipse Memory Analyzer生成的泄漏分析报告
attachment:


heap_11-22_Leak_Suspects.rar

aspen13
2010-11-25 11:12

以前也遇到过Tomcat内存泄露的问题,运行一段时间或大量并发几分钟,Tomcat就挂掉了。

最后查到原因是Struts2配置:struts.configuration.xml.reload = true 的问题。

设置struts.configuration.xml.reload=true,导致线程出现阻塞,资源得不到释放,最终出现内存溢出。