РЕДАКТИРОВАТЬ 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 и Spring (Data) Jpa bump. Мы также перешли на Tomcat 7.0.39. CGLIB (который не упоминался ранее, но был включен) также был загружен до 3.0
Вот некоторые из вещей, которые я пытался устранить, но не повезло:
- Изменен наш POM (с использованием Maven), чтобы настроить наш драйвер базы данных как имеющийся
- Добавлена зависимость для SLF4J к включению jul-to-slf4j, поскольку было упомянуто, что здесь может быть потенциальная утечка.
- Протестируйте защитник утечки Classload (https://github.com/mjiderhamn/classloader-leak-prevention). Это, похоже, сработало (поскольку оно спамало мои журналы во время undeploy/shutdown, указав тонну вещей, которые он чистил), но после использования VisualVM мое пространство perm gen не было исправлено. Это может быть полезно для других...
- Попробовал перечислить все исключения в моей POM с помощью инструмента анализа зависимостей Maven (в IntelliJ в окне Maven Projects вы можете щелкнуть правой кнопкой мыши зависимости и выбрать "Показать зависимости" и "Сменить-Удалить все красные зависимости" ). Это не помогло, но уменьшило размер моего WAR файла примерно на миллион или около того.
-
Реализована конфигурация сохраняемости JPA следующим образом (обратите внимание на комментарии) на основе неразрешенного отчета об ошибке от Spring (https://jira.springsource.org/browse/SPR-9274):
@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; }
- Создал "ThreadImmolator" согласно qaru.site/info/123728/... в следующем SO-вопросе: Возможно ли это создать утечка памяти в Tomcat?. Похоже, что это почти то же самое, что и обнаружение TreadLocal Leak Detection выше - у меня не было жалобщика Tomcat Leak; однако пространство perm gen по-прежнему не восстанавливалось после повторного развертывания.
- Моя первая попытка состояла в том, чтобы добавить @PreDestory в мой WebConfig (тот же bean, который имеет @EnableWebMvc), чтобы попытаться вызвать его при закрытии. Химическая завивка Генерал остался.
- Моя вторая попытка заключалась в подклассе ContextLoaderListener, переопределении функций ContextDestoryed() и inline. Химическая завивка Генерал остался.
- Так как ThreadImmolator не помог (или оказался не помог), я попробовал решение, предложенное в qaru.site/info/123504/... в следующем SO-вопросе: 'Как очистить 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.
В этот момент у меня закончились идеи.
Я также попытался изучить анализ кучи, используя это как отправную точку (http://cdivilly.wordpress.com/2012/04/23/permgen-memory-leak/). Я могу найти загрузчик классов, который не был очищен, и я вижу, что он все еще ссылается на все классы, связанные с Spring. Я также пробовал поиск org.springframework.core.NamedThreadLocal
, и я все еще вижу, как они появляются в куче после сдачи дампа после запуска ThreadImmolator, Thread Leak Preventor и других "жестких решений", которые я пробовал выше.
Возможно, приведенная выше информация может помочь кому-то, но я продолжу пересматривать эту SO с новой информацией или решить эту проблему.
Проблема
У приложения нет проблем, запущенных в течение нескольких дней подряд на рабочем сервере, но когда я выполняю развертывание для обновлений, программа Tomcat Manager будет жаловаться на утечки (если я нажимаю на поиск утечек). Если я выполняю 6-10 разводок, в конечном итоге у Tomcat заканчивается память с ошибкой памяти PermGen, и мне нужно перезапустить службу Tomcat, и все возвращается в нормальное состояние.
Когда я запускаю/отлаживаю приложение локально и выполняю некоторые действия, требующие доступа через Jpa/Hibernate (т.е. я вхожу в систему или запрашиваю список из JpaRepository), а затем выключение приложения, я получаю следующие сообщения в своем отладочном выходе от Tomcat:
Октябрь 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: веб-приложение [/] создал ThreadLocal с ключом типа [org.springframework.core.NamedThreadLocal] (значение [Transactional ресурсы]) и значение типа [java.util.HashMap] (значение [{public abstract java.lang.Object org.springframework.data.repository.CrudRepository.findOne(java.io.Serializable) [email protected]}]) но не удалось удалить его, когда веб-приложение было остановлено. Потоки со временем будут обновляться, чтобы попытаться избежать вероятной памяти протечь.
Октябрь 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: веб-приложение [/] создал ThreadLocal с ключом типа [org.springframework.core.NamedThreadLocal] (значение [Transactional ресурсы]) и значение типа [java.util.HashMap] (значение [{public abstract java.util.List org.springframework.data.jpa.repository.JpaRepository.findAll() = [email protected]}]) но не удалось удалить его, когда веб-приложение было остановлено. Потоки со временем будут обновляться, чтобы попытаться избежать вероятной памяти протечь.
Октябрь 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: веб-приложение [/] создал ThreadLocal с ключом типа [org.springframework.core.NamedThreadLocal] (значение [Transactional ресурсы]) и значение типа [java.util.HashMap] (значение [{public абстрактный java.lang.Iterable org.springframework.data.querydsl.QueryDslPredicateExecutor.findAll(com.mysema.query.types.Predicate) [email protected]}]) но не удалось удалить его, когда веб-приложение было остановлено. Потоки со временем будут обновляться, чтобы попытаться избежать вероятной памяти протечь.
Октябрь 03, 2012 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: веб-приложение [/] создал ThreadLocal с ключом типа [org.springframework.core.NamedThreadLocal] (значение [Transactional ресурсы]) и значение типа [java.util.HashMap] (значение [{public abstract data.domain.UserAccount UserAccountRepository.findByUserName(java.lang.String) [email protected]}]) но не удалось удалить его, когда веб-приложение было остановлено. Потоки со временем будут обновляться, чтобы попытаться избежать вероятной памяти протечь.
и т.д. и т.д.
Конфигурация
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> {
}
Все мои репозитории доступны через класс службы, который зарегистрирован как @Component в Spring. Это делается для удаления доступа к репозиторию с контроллеров 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>
Вопросы
- Что вызывает утечку памяти и как ее исправить?
- Как я могу отладить эту проблему?
- Есть ли что-нибудь в моем конфигурационном наборе, которое можно улучшить?
У меня действительно не хватает идей по решению этой проблемы.