編集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 の依存関係を追加して jul-to-slf4j を含めました
- Classload Leak Protector ( https://github.com/mjiderhamn/classloader-leak-prevention )を試してみました。これは機能しているように見えましたが (アンデプロイ/シャットダウン中に、クリーンアップしていた大量のものをリストすることでログをスパム送信したため)、VisualVM を使用した後、perm gen スペースが再利用されませんでした。これは他の人にとって役立つかもしれません...
- Maven の依存関係分析ツールを使用して、POM のすべての除外を一覧表示しようとしました (Maven プロジェクト ウィンドウの IntelliJ では、依存関係を右クリックして [依存関係を表示] を選択し、すべての赤い依存関係を Shift-Delete できます)。これは役に立ちませんでしたが、WAR ファイルのサイズが約 1 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」を作成しました:「これはTomcatでメモリリークを引き起こす可能性が非常に高いですか?'。これは、上記の TreadLocal リーク検出とほぼ同じことを行っているように見えました。Tomcat リーク検出器には文句はありませんでした。ただし、perm gen スペースは再デプロイ後も回復されませんでした。
- 私の最初の試みは、WebConfig (@EnableWebMvc を持つ同じ Bean) に @PreDestory を追加して、閉じるときにトリガーしようとすることでした。パーマ。ゲンは残った。
- 私の 2 番目の試みは、ContextLoaderListener をサブクラス化し、ContextDestoryed() をオーバーライドして、関数をインライン化することでした。パーマ。ゲンは残った。
- ThreadImmolator が役に立たなかった (または役に立たないように見えた) ため、https ://stackoverflow.com/a/16644476 で提案されている解決策を次の SO の質問で試しました:「threadlocals をクリーンアップする方法」。これにより、次のことを試すようになりました: ' および ' 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 からのデバッグ出力に次のメッセージが表示されます。
2012 年 10 月 3 日 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: Web アプリケーション [/] は、タイプ [org.springframework.core.NamedThreadLocal] のキーを持つ ThreadLocal を作成しました (値 [トランザクション リソース] ) および [java.util.HashMap] 型の値 (value [{public abstract java.lang.Object org.springframework.data.repository.CrudRepository.findOne(java.io.Serializable)=java.lang.Object@842e211 }]) ですが、Web アプリケーションが停止したときに削除できませんでした。スレッドは、メモリ リークの可能性を回避するために、時間の経過とともに更新されます。
2012 年 10 月 3 日 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: Web アプリケーション [/] は、タイプ [org.springframework.core.NamedThreadLocal] のキーを持つ ThreadLocal を作成しました (値 [トランザクション リソース] ) および [java.util.HashMap] タイプの値 (値 [{public abstract java.util.List org.springframework.data.jpa.repository.JpaRepository.findAll()=java.lang.Object@842e211}])しかし、Web アプリケーションが停止したときにそれを削除できませんでした。スレッドは、メモリ リークの可能性を回避するために、時間の経過とともに更新されます。
2012 年 10 月 3 日 2:55:13 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: Web アプリケーション [/] は、タイプ [org.springframework.core.NamedThreadLocal] のキーを持つ ThreadLocal を作成しました (値 [トランザクション リソース] ) および [java.util.HashMap] 型の値 (value [{public abstract 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 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks SEVERE: Web アプリケーション [/] は、タイプ [org.springframework.core.NamedThreadLocal] のキーを持つ ThreadLocal を作成しました (値 [トランザクション リソース] ) およびタイプ [java.util.HashMap] (値 [{public abstract 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> {
}
すべてのリポジトリは、Spring で @Component として登録されている Service クラスを介してアクセスされます。これは、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>
質問
- メモリ リークの原因とその修正方法を教えてください。
- この特定の問題をデバッグするにはどうすればよいですか?
- 構成セットに改善できるものはありますか?
この問題を解決するためのアイデアが本当に尽きてしまいました。