【问题标题】:Tomcat 7 memory leak with spring-mvc + jpa/hibernate + mysqlTomcat 7 内存泄漏与 spring-mvc + jpa/hibernate + mysql
【发布时间】:2014-02-25 23:23:35
【问题描述】:

我正在使用

构建一个相对简单的网络应用程序
  • spring-mvc 3.2.0
  • spring-jpa 1.2.0
  • jpa 2.0
  • 休眠 3.6
  • MySql 5.6(使用mysql jdbc驱动5.1.28)

我自己不初始化连接,只使用自动装配的存储库来读取或写入实体。

问题:每次:

  1. 应用已停止(重新部署等)
  2. 用户会话过期

我在 tomcat 服务器和以下 catalina 日志上遇到内存泄漏:

Feb 02, 2014 1:31:12 PM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc 
SEVERE: The web application [/test] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
Feb 02, 2014 1:31:12 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads 
SEVERE: The web application [/test] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak.
Feb 02, 2014 1:31:12 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks 
SEVERE: The web application [/test] created a ThreadLocal with key of type [org.springframework.core.NamedThreadLocal] (value [Transactional resources]) and a value of type [java.util.HashMap] (value [{public abstract java.util.List org.springframework.data.jpa.repository.JpaRepository.findAll()=java.lang.Object@632cc9ed, public abstract java.util.List data.repository.QuestionRepository.findAllNonDisabled()=java.lang.Object@632cc9ed, public abstract java.util.List data.repository.AnswerRepository.findAllAnswersForUserAndGameOrderBySortIndex(int,int)=java.lang.Object@632cc9ed, public abstract java.lang.Object org.springframework.data.repository.CrudRepository.save(java.lang.Object)=java.lang.Object@632cc9ed, public abstract java.lang.Object org.springframework.data.repository.CrudRepository.findOne(java.io.Serializable)=java.lang.Object@632cc9ed}]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
Feb 02, 2014 1:31:12 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks
.......
SEVERE: The web application [/test] created a ThreadLocal with stop
INFO: The stop() method was called on component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/test]] after stop() had already been called. The second call will be ignored.
Feb 02, 2014 1:42:26 PM org.apache.catalina.startup.HostConfig undeploy
INFO: Undeploying context [/test]

此日志内容:曾经对数据库执行的任何查询(包括 findOne 和 findAll 等默认查询)都保存在内存中,无法卸载。

我已经阅读了一些解决方法 (like moving the mysql driver into the lib folder of tomcat ),但我不认为这是一个普遍问题,我认为这是由我的应用程序配置造成的。

这是我的配置:

Servlet 配置 web.xml

<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>GPMM 2014 Feedback</display-name>

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- Spring Security -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/security.xml</param-value>
</context-param>

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/resources/*</url-pattern>
</servlet-mapping>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring Security -->

<!-- File upload -->
<filter>
    <filter-name>multipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/admin/**</url-pattern>
</filter-mapping>
<!-- File upload -->

Spring mvc bean 配置:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<context:component-scan base-package="com.springapp.mvc.controller.admin"/>
<context:component-scan base-package="com.springapp.mvc.controller.user"/>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="2000000"/> <!-- File size in bytes. -->
</bean>

<!-- Data access management -->
<jpa:repositories base-package="data.repository"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="defaultPersistenceUnit"/>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Data access maanagement -->

安全配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/security
   http://www.springframework.org/schema/security/spring-security.xsd">

<http auto-config="true" authentication-manager-ref="adminauth">
    <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
    <intercept-url pattern="/updatedb/**" access="ROLE_DBADMIN" />
    <!--<intercept-url pattern="/game/**" access="ROLE_USER" />-->
    <form-login login-page="/adminlogin" authentication-failure-url="/accessdenied" />
    <logout logout-success-url="/admin" logout-url="/j_spring_security_logout" />
</http>

<authentication-manager id="adminauth">
    <authentication-provider>
        <password-encoder hash="sha-256"/>
        <user-service>
            <user name="admin" password="xxx" authorities="ROLE_ADMIN" />
            <user name="dbadmin" password="xxx" authorities="ROLE_DBADMIN" />
            <user name="user" password="xxx" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

</beans:beans>

和 JPA/Hibernate 配置:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>data.model.TestEntity</class>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
        <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/gpm" />
        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
        <property name="hibernate.connection.username" value="user" />
        <property name="hibernate.connection.password" value="password" />
        <property name="hibernate.hbm2ddl.auto" value="validate" />
    </properties>
</persistence-unit>

这是一个典型的代码示例:

@Controller
public class TestController
{
static final Logger logger = LogManager.getLogger(TestController.class);

@Autowired
private TestRepository testRepository;


@RequestMapping(value = "/admin/tests", method = RequestMethod.GET)
public String listTests(ModelMap model)
{
    TestEntity gamegroup = new TestEntity();

    model.addAttribute("test", test);
    model.addAttribute("tests", testRepository.findAll());

    return "admin/tests";
}

@RequestMapping(value = "/admin/tests/update", method = RequestMethod.POST)
public String updateTest(@ModelAttribute("test") TestEntity test, BindingResult result)
{
    testRepository.save(test);

    return "redirect:/admin/tests";
}
}

请指出我的配置或访问 mysql 的方式可能有什么问题。

【问题讨论】:

  • 问题出在 MySQL 线程上。见here
  • 谢谢,我已经看过你的帖子,实际上已经实现了监听器,现在正在测试它,但是我有 4 个不同的泄漏(jdbc、事务、连接和一个我还不明白)。有时我觉得我上辈子得罪了神
  • 看起来解决方案解决了jdbc和连接问题,不幸的是不是事务一:(

标签: hibernate tomcat spring-mvc jpa memory-leaks


【解决方案1】:

线程本地问题 我正在追踪一个类似的问题,您应该做的一件事是将@Transactional 添加到任何涉及您的数据库的请求映射中。这使得这些交互具有事务性,因此如果出现问题,它们可以回滚。见This blog post

WebClassLoader 问题 您将驱动程序加载到 servlet 中,而不是由容器(Tomcat)管理。您可以在关闭时在侦听器中自行卸载它,也可以让 Tomcat 管理它。关于走哪条路有很多争论。我喜欢自己管理它,以使管理员的管理更容易。但是,对于知道自己在做什么的管理员来说,它就没有那么灵活了。见this stack overflow

【讨论】:

猜你喜欢
  • 2015-06-04
  • 2014-04-03
  • 2016-02-21
  • 1970-01-01
  • 2012-09-24
  • 2015-02-13
  • 1970-01-01
  • 2014-12-25
  • 2017-12-16
相关资源
最近更新 更多