【问题标题】:SpringMVC + Hibernate + Tomcat leaks memory?SpringMVC + Hibernate + Tomcat 内存泄漏?
【发布时间】:2014-04-03 17:23:54
【问题描述】:

当我关闭我的 web 应用程序时。 Tomcat 不断显示 SEVERE 警告:

02-Mar-2014 23:07:30.890 SEVERE [http-apr-8080-exec-4]  org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks The web application   [/movie-collection-0.0.2] created a ThreadLocal with
key of type [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1] (value [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1@520a38a8]) and a value of type [com.microsoft.sqlserver.jdbc.ActivityId] (val
ue [3488cbb4-f0e2-4505-93c4-78248b161847-2]) 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.
02-Mar-2014 23:07:30.891 SEVERE [http-apr-8080-exec-4]  org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks The web application [/movie-collection-0.0.2] created a ThreadLocal with
key of type [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1] (value  [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1@520a38a8]) and a value of type  [com.microsoft.sqlserver.jdbc.ActivityId] (val
ue [ba8aeba9-de9e-49de-8732-5290ae65167b-0]) 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.
02-Mar-2014 23:07:31.848 INFO [http-apr-8080-exec-4] org.apache.catalina.startup.HostConfig.undeploy Undeploying context [/movie-collection-0.0.2]

我清楚地看到描述问题原因的消息。但是在挣扎了5个小时后,我仍然无法消除它。这是我的持久性配置:

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class PersistenceContext {

private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";

private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT = "hibernate.use_sql_comments";
private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";

private static final String PROPERTY_NAME_JDBC_BATCH_SIZE = "hibernate.jdbc.batch_size";
private static final String PROPERTY_NAME_CACHE_PROVIDER_CLASS = "hibernate.cache.provider_class";

private static final String PROPERTY_NAME_C3P0_MIN_SIZE = "hibernate.c3p0.min_size";
private static final String PROPERTY_NAME_C3P0_MAX_SIZE = "hibernate.c3p0.max_size";
private static final String PROPERTY_NAME_C3P0_TIMEOUT_SIZE = "hibernate.c3p0.timeout";
private static final String PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE = "hibernate.c3p0.max_statements";
private static final String PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE = "hibernate.c3p0.idle_test_period";

private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

@Resource
private Environment environment;

@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
    dataSource.setUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
    dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
    dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
    return dataSource;
}

@Bean
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setHibernateProperties(getHibernateProperties());
    sessionFactory.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
    return sessionFactory;
}

@Bean
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory().getObject());
    return transactionManager;
}

private Properties getHibernateProperties() {
    Properties props = new Properties();
    props.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
    props.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
    props.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
    props.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
    props.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
    props.put(PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT));
    props.put(PROPERTY_NAME_JDBC_BATCH_SIZE, environment.getRequiredProperty(PROPERTY_NAME_JDBC_BATCH_SIZE));
    props.put(PROPERTY_NAME_CACHE_PROVIDER_CLASS, environment.getRequiredProperty(PROPERTY_NAME_CACHE_PROVIDER_CLASS));
    props.put(PROPERTY_NAME_C3P0_MIN_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MIN_SIZE));
    props.put(PROPERTY_NAME_C3P0_MAX_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MAX_SIZE));
    props.put(PROPERTY_NAME_C3P0_TIMEOUT_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_TIMEOUT_SIZE));
    props.put(PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE));
    props.put(PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE));

    return props;
}
}

还有我的属性:

#Database Configuration
 db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
 db.url=jdbc:sqlserver://localhost;integratedSecurity=true;databaseName=Abc
 db.username=""
 db.password=""

#Hibernate Configuration
 hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
 hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
 hibernate.cache.provider_class=org.hibernate.cache.internal.NoCachingRegionFactory
 hibernate.hbm2ddl.auto=validate
 hibernate.jdbc.batch_size=50

#Connection pool config
 hibernate.c3p0.min_size=2
 hibernate.c3p0.max_size=20
 hibernate.c3p0.timeout=300
 hibernate.c3p0.max_statements=50
 hibernate.c3p0.idle_test_period=3000

#SQL output format
 hibernate.format_sql=true
 hibernate.show_sql=true
 hibernate.use_sql_comments=false

#Declares the base package of the entity classes
 entitymanager.packages.to.scan=com.adc.domain

我会很感激任何想法。

更新

我添加了带有适当侦听器的 ServletContextListener 以取消注册 JDBC 驱动程序。还是没有结果... 我想我得想办法清除那些 ThreadLocals

public class DbConnectionCleaner implements ServletContextListener {

private static final Logger log = LogManager.getLogger(DbConnectionCleaner.class);

@Override
public void contextInitialized(ServletContextEvent sce) {
    log.info("Servlet context initialized");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    log.info("Cleaning DB connections");
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        try {
            DriverManager.deregisterDriver(driver);
            log.info(String.format("Deregistering jdbc driver: %s", driver));
        } catch (SQLException ex) {
            log.error(String.format("Error deregistering driver %s", driver), ex);
        }

    }
}
}

【问题讨论】:

  • 这会导致明显的问题,还是只是您在日志中注意到的问题?它可能只是 MS 驱动程序无害(但令人讨厌)在退出之前没有自行清理;或错误检测的泄漏。无论如何,它说线程会随着时间的推移而更新以避免可能的泄漏,所以它看起来对你来说处理这个问题很好。

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


【解决方案1】:

如果您的 JDBC 驱动程序的 JAR 文件位于 Tomcat 的 lib/ 目录中,那么应该没有泄漏:您不会将 WebappClassLoader 固定在内存中。另请注意,Tomcat 通过回收(即销毁和重新创建)线程池中的所有线程以删除绑定到它们的 ThreadLocals 来保护您免受问题的影响。

说实话,这是 JDBC 驱动程序中的一个错误:在执行终止后,不应保持任何 ThreadLocals 绑定到任何线程。您可能需要与 Microsoft 联系,看看他们是否有修复此问题的更新。您还可以检查您是否正在使用某种需要使用 ThreadLocal 的功能——这与“活动相关器”有关。如果您禁用该功能,您可能会避免该错误。

【讨论】:

猜你喜欢
  • 2014-12-25
  • 2014-02-25
  • 2015-12-27
  • 1970-01-01
  • 2017-08-31
  • 1970-01-01
  • 1970-01-01
  • 2012-08-20
  • 2012-08-06
相关资源
最近更新 更多