停止或重新部署时发生内存泄漏-Spring 3.1.2,Hibernate 4.1.0,Spring Data-Jpa 1.1.0,Tomcat 7.0.30 - java

编辑1

2013/06/07-虽然我仍然遇到此问题,但它仍然只影响重新部署。自从最初的问题发布以来,我们已经进行了一些升级。这是我们的新版本(仍显示手头的问题):

<properties>
    <!-- Persistence and Validation-->
    <hibernate.version>4.1.0.Final</hibernate.version>
    <hibernate.jpa.version>1.0.1.Final</hibernate.jpa.version>
    <javax.validation.version>1.0.0.GA</javax.validation.version>
    <querydsl.version>2.2.5</querydsl.version>
    <spring.jpa.version>1.2.0.RELEASE</spring.jpa.version>
    <spring.ldap.version>1.3.1.RELEASE</spring.ldap.version>

    <!-- Spring and Logging -->
    <spring.version>3.1.3.RELEASE</spring.version>
    <spring.security.version>3.1.3.RELEASE</spring.security.version>
    <slf4j.version>1.6.4</slf4j.version>
    <jackson.version>1.9.9</jackson.version>

    <cglib.version>3.0</cglib.version>
</properties>

如您所见,它基本上只是一个Spring Framework凹凸和Spring(Data)Jpa凹凸。我们还升级到了Tomcat 7.0.39。 CGLIB(之前未提及但已包含在内)也已升至3.0

以下是一些我尝试过但不能靠运气解决的问题:

更改了我们的POM(使用Maven)以将数据库驱动程序设置为具有所提供的范围
由于提到SLF4J可能存在潜在的泄漏,因此增加了对SLF4J的依赖,使其包含jul-to-slf4j
尝试了Classload泄漏保护器(https://github.com/mjiderhamn/classloader-leak-prevention)。这似乎奏效了(因为它在取消部署/关闭期间通过列出要清除的大量内容来向我的日志发送垃圾邮件),但是在使用VisualVM之后,没有回收我的烫发生成空间。这可能对其他人有用...
试图使用Maven的依赖性分析工具列出我的POM中的所有排除项(在Maven项目窗口中的IntelliJ中,您可以右键单击依赖性,然后选择“显示依赖性”,然后按住Shift删除所有红色依赖性)。这没有帮助,但是它使我的WAR文件大小减少了大约MB。
根据来自Spring(https://jira.springsource.org/browse/SPR-9274)的未解决的错误报告,如下重构JPA Persistence配置(注意注释):

@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { // Important line (notice entityManagerFactory is 'provided/autowired'
    return new JpaTransactionManager(entityManagerFactory);
}

@Bean
public EntityManagerFactory getEntityManagerFactory(DataSource dataSource) { // Important line (notice dataSource is 'provided/autowired'

    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setPackagesToScan("my.scanned.domain");

    AbstractJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);
    vendorAdapter.setShowSql(false);
    vendorAdapter.setDatabase(Database.POSTGRESQL);
    vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL82Dialect");

    factoryBean.setJpaVendorAdapter(vendorAdapter);

    Map<String, Object> properties = new HashMap<>();
    properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
    properties.put( "hibernate.bytecode.provider", "cglib" );   // Suppose to help java pergem space issues with hibernate

    factoryBean.setPersistenceProvider(new HibernatePersistence());
    factoryBean.setJpaPropertyMap(properties);
    factoryBean.setPersistenceUnitName("myPersistenace");
    factoryBean.afterPropertiesSet();

    return factoryBean.getObject(); // Important line
}

@Bean
public PersistenceExceptionTranslator getHibernateExceptionTranslator() { // Required
    return new HibernateExceptionTranslator();
}

@Bean
public DataSource getDataSource() {
    JndiDataSourceLookup lookup = new JndiDataSourceLookup();
    DataSource dataSource = lookup.getDataSource("java:comp/env/jdbc/myLookup");

    lookup = null;

    return dataSource;
}

在以下SO问题中,根据https://stackoverflow.com/a/15710827/941187创建了一个'ThreadImmolator'。这似乎与上面的TreadLocal泄漏检测几乎一样-我没有Tomcat泄漏检测器在抱怨;但是,重新部署后,烫发源空间仍未恢复。

我的第一次尝试是在WebConfig中添加一个@PreDestory(具有@EnableWebMvc的同一bean),以尝试在关闭时触发它。烫发根仍然存在。
我的第二次尝试是将ContextLoaderListener子类化,重写ContextDestoryed()并内联函数。烫发根仍然存在。

由于ThreadImmolator没有帮助(或似乎没有帮助),因此我尝试了以下问题中Is this very likely to create a memory leak in Tomcat?中建议的解决方案:'https://stackoverflow.com/a/16644476'。这使我尝试:'How to clean up threadlocals'和'https://weblogs.java.net/blog/jjviana/archive/2010/06/09/dealing-glassfish-301-memory-leak-or-threadlocal-thread-pool-bad-ide'。

在这一点上,我的想法已经用完了。

我还尝试过学习以此为起点(http://blog.igorminar.com/2009/03/identifying-threadlocal-memory-leaks-in.html)的堆分析。我可以找到尚未清理的类加载器,并且可以看到它仍在引用与Spring相关的所有类。我也尝试过搜索org.springframework.core.NamedThreadLocal,运行ThreadImmolator,Thread Leak Preventor和我上面尝试过的其他“笨手解决方案”后,在转储后仍然可以看到它们显示在堆中。

也许以上信息可能会对某人有所帮助,但是我将继续通过新信息或者如果我解决问题来重新访问此SO。

问题

该应用程序在生产服务器上连续运行几天都没有问题,但是当我执行更新部署时,Tomcat Manager程序将抱怨泄漏(如果我单击“查找泄漏”)。如果执行6-10次部署,最终Tomcat会耗尽内存,并出现PermGen内存错误,我需要重新启动Tomcat服务,一切都会恢复正常。

当我在本地运行/调试应用程序并执行一些需要通过Jpa / Hibernate访问的操作(即,我登录或从JpaRepository请求列表)然后关闭该应用程序时,我在Tomcat的调试输出中收到以下消息:

2012年10月3日2:55:13 PM org.apache.catalina.loader.WebappClassLoader
checkThreadLocalMapForLeaks严重:Web应用程序[/]
用键类型创建了一个ThreadLocal
[org.springframework.core.NamedThreadLocal](值[Transactional
资源])和类型[java.util.HashMap]的值(值[{public
抽象java.lang.Object
org.springframework.data.repository.CrudRepository.findOne(java.io.Serializable)=java.lang.Object@842e211}])
但是在Web应用程序停止时无法将其删除。线程数
将随着时间的流逝而更新,以尝试避免可能的记忆
泄漏。

2012年10月3日下午2:55:13
org.apache.catalina.loader.WebappClassLoader
checkThreadLocalMapForLeaks严重:Web应用程序[/]
用键类型创建了一个ThreadLocal
[org.springframework.core.NamedThreadLocal](值[Transactional
资源])和类型[java.util.HashMap]的值(值[{public
抽象java.util.List
org.springframework.data.jpa.repository.JpaRepository.findAll()= java.lang.Object@842e211}])
但是在Web应用程序停止时无法将其删除。线程数
将随着时间的流逝而更新,以尝试避免可能的记忆
泄漏。

2012年10月3日下午2:55:13
org.apache.catalina.loader.WebappClassLoader
checkThreadLocalMapForLeaks严重:Web应用程序[/]
用键类型创建了一个ThreadLocal
[org.springframework.core.NamedThreadLocal](值[Transactional
资源])和类型[java.util.HashMap]的值(值[{public
抽象java.lang.Iterable
org.springframework.data.querydsl.QueryDslPredicateExecutor.findAll(com.mysema.query.types.Predicate)=java.lang.Object@842e211}])
但是在Web应用程序停止时无法将其删除。线程数
将随着时间的流逝而更新,以尝试避免可能的记忆
泄漏。

2012年10月3日下午2:55:13
org.apache.catalina.loader.WebappClassLoader
checkThreadLocalMapForLeaks严重:Web应用程序[/]
用键类型创建了一个ThreadLocal
[org.springframework.core.NamedThreadLocal](值[Transactional
资源])和类型[java.util.HashMap]的值(值[{public
抽象的data.domain.UserAccount
UserAccountRepository.findByUserName(java.lang.String)=java.lang.Object@842e211}])
但是在Web应用程序停止时无法将其删除。线程数
将随着时间的流逝而更新,以尝试避免可能的记忆
泄漏。

等等等

组态

Spring是通过注释配置的,我还将Postgres 8.4用作数据库后端。

JPA通过注释进行配置(jpa-repository-context.xml只是说要查找此类):

@Configuration
@EnableTransactionManagement
@ImportResource( "classpath*:*jpa-repository-context.xml" )
@ComponentScan( basePackages = { "data.repository" } )
public class PersistenceJpaConfig
{
    @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory()
        {
            LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
            factoryBean.setDataSource( dataSource() );
            factoryBean.setPackagesToScan( new String[] { "data.domain" } );

            // Setup vendor specific information. This will depend on the chosen DatabaseType
            HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
            vendorAdapter.setGenerateDdl( true );
            vendorAdapter.setShowSql( false );
            vendorAdapter.setDatabasePlatform( "org.hibernate.dialect.PostgreSQL82Dialect" );

            factoryBean.setJpaVendorAdapter( vendorAdapter );

            Map<String, Object> properties = new HashMap<String, Object>();
            properties.put( "hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy" );

            factoryBean.setJpaPropertyMap( properties );

            return  factoryBean;
        }

        @Bean
        public DataSource dataSource()
        {
            JndiDataSourceLookup lookup = new JndiDataSourceLookup();
            DataSource dataSource;

            dataSource = lookup.getDataSource( "java:comp/env/jdbc/postgres" );


            return dataSource;
        }

        @Bean
        public PlatformTransactionManager transactionManager()
        {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory( entityManagerFactory().getObject() );

            return transactionManager;
        }
}

示例存储库:

public interface UserAccountRepository extends JpaRepository<UserAccount, Long>, QueryDslPredicateExecutor<UserAccount> {
}

我的所有存储库都可以通过Service类访问,该服务类在Spring中注册为@Component。这样做是为了从Spring控制器中删除存储库访问:

@Component
public class UserAccountService {

    @Autowired
    private UserAccountRepository userAccountRepository;

    public List<UserAccount> getUserAccounts() {
        return userAccountRepository.findAll();
    }
    ...
}

以下是Maven pom.xml中使用的各种组件的版本:

<properties>
        <!-- Persistence and Validation-->
        <hibernate.version>4.1.0.Final</hibernate.version>
        <hibernate.jpa.version>1.0.1.Final</hibernate.jpa.version>
        <javax.validation.version>1.0.0.GA</javax.validation.version>
        <querydsl.version>2.2.5</querydsl.version>
        <spring.jpa.version>1.1.0.RELEASE</spring.jpa.version>

        <!-- Spring and Logging -->
        <spring.version>3.1.2.RELEASE</spring.version>
        <spring.security.version>3.1.2.RELEASE</spring.security.version>
        <slf4j.version>1.6.4</slf4j.version>
        <jackson.version>1.9.9</jackson.version>

        <!-- Testing Suites -->
        <selenium.version>2.24.1</selenium.version>
</properties>

问题

是什么导致内存泄漏,我该如何解决?
我将如何调试此特定问题?
我的配置集中有什么可以改进的地方吗?

我真的没有解决这个问题的想法。

参考方案

我认为您可能同时发生两种泄漏。 Spring警告您有关正常的“堆”内存泄漏。这还没有给您造成问题,因为...您的重新部署还导致PermGen的使用过多,而这个问题首先困扰您。有关第二种泄漏的信息,请参见Dealing with "java.lang.OutOfMemoryError: PermGen space" error [感谢duffymo]

[更新]

由于您说上述链接中的建议没有帮助,因此我能想到的唯一其他建议是:

确保弹簧关闭时您的四季豆正在清理自己
每个在构造函数或init方法中分配资源(任何不可能被垃圾回收的资源)的bean都应该有一个destroy方法来取消分配它
对于任何引用spring-data模块中任何类的bean来说尤其如此,catalina抱怨此模块中的类
增加您的置换空间。即使此建议不能解决问题,添加以下内容也应使这些故障的发生频率降低。

尝试-XX:MaxPermSize = 256m,如果仍然存在,请尝试-XX:MaxPermSize = 512m

最终的超级暴力方法是逐步将内容剥离到您的应用程序中,直到问题消失。这将帮助您缩小范围,以便可以确定是代码问题还是Spring,Hibernate等中的错误。

Spring Data Cassandra的事务管理 - java

我正在使用Spring和Cassandra作为基础数据库。曾提到过弹簧伞项目“ spring data cassandra”。与休眠不同,在这里无法找到如何管理事务。如果您中的某些人已经合并,请共享要包含的事务管理器的详细信息。 参考方案 Cassandra不支持传统(ACID)的事务。在某些特殊情况下,可以通过一些构造来实现事务原子性,例如原子批处理(请参…

在Spring框架中,从数据库中获取保存的实体,以与修改后的未保存实体进行比较 - java

使用Java和Spring,我有一个已修改但尚未持久化的实体。我想将其与其原始状态(数据库中仍然存在的状态)进行比较。但是,当我为实体获取旧状态时,它总是以修改后的状态返回实体。下面显示了一个示例,以更好地说明这一点。MyEntity类代表实体。MyEntityRepository类是用于为MyEntity实体执行数据库交互的Spring Data Repo…

Java:“自动装配”继承与依赖注入 - java

Improve this question 我通常以常见的简单形式使用Spring框架: 控制器服务存储库通常,我会在CommonService类中放一个通用服务,并使所有其他服务扩展到类中。一个开发人员告诉我,最好在每个服务中插入CommonClass而不是使用继承。我的问题是,有一个方法比另一个更好吗? JVM或性能是否会受到另一个影响?更新资料Comm…

将自己的方法应用于h2 - java

我已经开始学习Spring,并且正在尝试将电影的标题,类型,年份和演员数据存储在数据库(h2)中。如何使用我自己的代码中的方法从该数据库中获取数据,以使用system.out.println()在控制台中将其打印出来?我有Movie和Actor的类及其相应的属性,都用@Entity批注标记。接下来,我有一个扩展JpaRepository的接口(我知道有找到所…

Spring Boot:java.time.Duration的默认序列化从字符串更改为数字 - java

我们最近从Spring Boot 2.1.9升级到2.2.1,这导致我们的测试失败。调查导致结果,默认情况下java.time.Duration类型现在序列化为不同的序列。现在,我们将得到"PT15M",而不是在JSON消息中包含字符串"900.0"。 POJO定义如下所示@JsonProperty(required …