编辑 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
以下是我在没有运气的情况下尝试解决手头问题的一些方法:
@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;
}
在这一点上,我已经没有想法了。
我还尝试以此为起点来学习堆分析( http://cdivilly.wordpress.com/2012/04/23/permgen-memory-leak/ )。我可以找到未被清理的类加载器,我可以看到它仍在引用与 Spring 相关的所有类。我也试过搜索
org.springframework.core.NamedThreadLocal
在运行 ThreadImmolator、Thread Leak Preventor 和我在上面尝试过的其他“笨手笨脚的解决方案”后进行转储后,我仍然可以看到它们出现在堆中。也许上述信息可能对某人有所帮助,但如果我解决了问题,我将继续使用新信息重新访问此 SO。
问题
该应用程序在生产服务器上连续运行数天没有问题,但是当我执行更新部署时,Tomcat 管理器程序会提示泄漏(如果我单击查找泄漏)。如果我执行 6-10 次部署,最终 Tomcat 会因 PermGen 内存错误而耗尽内存,我需要重新启动 Tomcat 服务,一切都会恢复正常。
当我在本地运行/调试应用程序并执行一些需要通过 Jpa/Hibernate 访问的操作(即,我登录或从 JpaRepository 请求列表)然后关闭应用程序时,我在 Tomcat 的调试输出中收到以下消息:
Oct 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: The web application [/] 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.lang.Object org.springframework.data.repository.CrudRepository.findOne(java.io.Serializable)=java.lang.Object@842e211}]) 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.
Oct 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: The web application [/] 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@842e211}]) 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.
Oct 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: The web application [/] 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.lang.Iterable org.springframework.data.querydsl.QueryDslPredicateExecutor.findAll(com.mysema.query.types.Predicate)=java.lang.Object@842e211}]) 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.
Oct 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: The web application [/] 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 data.domain.UserAccount UserAccountRepository.findByUserName(java.lang.String)=java.lang.Object@842e211}]) 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.
等等等等。
配置
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> {
}
我的所有存储库都通过在 Spring 中注册为 @Component 的 Service 类访问。这样做是为了从 Spring Controller 中删除存储库访问:
@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 警告您有关正常的“堆”内存泄漏。这还没有给你带来问题,因为......你的重新部署也导致了永久代的过度使用,这个问题首先袭击了你。有关第二种泄漏的信息,请参阅 Dealing with "java.lang.OutOfMemoryError: PermGen space" error [感谢达菲莫]
[更新]
由于您说上述链接中的建议没有帮助,因此我能想到的唯一其他建议是:
Try -XX:MaxPermSize=256m and if it persists, try -XX:MaxPermSize=512m
终极的 super 暴力方法是逐渐从你的应用程序中删除一些东西,直到问题消失。这将帮助您将范围缩小到可以确定它是您的代码问题还是 Spring、Hibernate 等中的错误的程度
关于java - 停止或重新部署时出现内存泄漏 - Spring 3.1.2、Hibernate 4.1.0、Spring Data-Jpa 1.1.0、Tomcat 7.0.30,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12717895/