2012-04-04 13 views
4

Spring、HibernateおよびJPAを使用するWebアプリケーションに問題が発生しました。問題は時間の経過と共に増加する非常に高いメモリ消費であり、決して減少しないように見える。 EntityManagerの使い方が間違っている可能性が高いです。私は周りを検索したが、私は確かに何かをまだ見つけていない。JPA EntityManagerの大きなメモリの問題

我々はすべて私たちの唯一のEntityManagerが注入され、次のGenericDAOを拡張するのDAOを使用している:

public abstract class GenericDAOImpl<E extends AbstractEntity<P>, P> implements 
    GenericDAO<E, P> { 

@PersistenceContext 
@Autowired 
private EntityManager entityManager; 
    [...] 

それがIDでエンティティを取得するためのメソッドを持っているので、痛みになりますどのので、ジェネリックDAOが使用されていますすべて〜40のDAOに実装する

のEntityManagerは、以下の方法でのSpring Beanとして設定されています

<bean class="org.springframework.orm.jpa.JpaTransactionManager" 
    id="transactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 
<tx:annotation-driven mode="aspectj" 
    transaction-manager="transactionManager" /> 
<bean 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
    id="entityManagerFactory"> 
    <property name="persistenceUnitName" value="persistenceUnit" /> 
    <property name="dataSource" ref="dataSource" /> 
</bean> 
<bean id="entityManager" factory-bean="entityManagerFactory" 
    factory-method="createEntityManager" scope="singleton" /> 

私が思う最大の問題はすべてのために、この共有EntityManagerを使用しています。サービスクラスでは、トランザクションを必要とするメソッドに対して@Transactionalアノテーションを使用しています。これは、読み込んだものから自動的にEntityManagerをフラッシュしますが、クリアとは異なりますので、オブジェクトはまだメモリ内にあると思います。

毎日発生するDB内のデータを自動的にインポートするたびにメモリが増加していることがわかりました(リンクされたオブジェクトが多数作成されている25k行あたり〜7ファイル)。しかし、通常の機能の間にも、たくさんのデータを取得するとき(要求のために一度に100〜200個のオブジェクトを考えてみましょう)。

誰も私が現在の状況をどのように改善できるか考えています(これは悪いので...)。

編集は:展開のアプリ上でプロファイラを実行し、これはそれが見つけたものですがあります:

One instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298" occupies 15,256,880 (20.57%) bytes. The memory is accumulated in one instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298". 

はこれがEntityManagerのがクリアされていない、おそらくですか?

+0

'PersistenceContext'を使用している間、私は' spring'と 'hibernate'と同じ問題を抱えているない真のJEEアプリケーションサーバです。あなたは漏れを解決しましたか? – oak

答えて

2

あなたの評価に同意するつもりです。 EntityManagers aren't really designed to be used as singletons。 EntityManagerをフラッシュしてもメモリからは何もクリアされませんが、エンティティはデータベースとのみ同期します。

EntityManagerは、永続コンテキスト内のすべてのオブジェクトへの参照を保持しており、決してコンテキストを閉じることはありません。 (This guyにも同様の問題がありました)それをクリアするとEntityManagerからエンティティへの参照はすべて削除されますが、clear()を呼び出す必要がある場合はEntityManagerの一般的な使い方を再評価する必要があります。 LazyInitializationExceptionsを避けたいのであれば、Spring *のOpenSessionInViewFilterを考慮してください。これにより、エンティティを遅延ロードしながら、SpringがBeanのライフサイクルを管理できるようになります。 Beanのライフサイクル管理はSpring Frameworkの大きな利点の1つです。そのため、その動作をオーバーライドすることが本当に必要なものであることを確認する必要があります。

実際には、永続的なEntityManagerが必要な場合がありますが、これらのケースは比較的少なく、適切に実装するために十分な理解が必要です。

注:OpenSessionInViewは、N+1 problemを避けるように十分注意してください。 some call Open Session in View an AntiPatternという大きな問題です。慎重に使用してください。

編集

また、あなたにも@Autowired@PersistenceContext要素に注釈を付ける必要はありません。 @PersistenceContextは配線自体を行います。

0

から@Autowired注釈を削除し、コンテキスト定義ファイルからentityManager bean定義を削除する必要があります。また、<context:annotation-config/><context:component-scan/>のXMLタグを使用しない場合は、コンテキスト内でPersistenceAnnotationBeanPostProcessor beanを定義する必要があります。

0

非JEE準拠のアプリケーションサーバーの場合は、@Autowired/@PersistenceContext private EntityManager entityManager;を使用しないでください。

あなたがやるべきことは、このようなものです:

class SomeClass { 
    @PersistenceUnit private EntityManagerFactory emf; 

    public void myMethod() { 
     EntityManager em = null; 
     try { 
     em = emf.createEntityManager(); 
     // do work with em 
     } 
    } catch (SomeExceptions e) { 
     // do rollbacks, logs, whatever if needed 
    } finally { 
     if (em != null && em.isOpen()) { 
     // close this sucker 
     em.clear(); 
     em.close(); 
     } 
    } 
} 

いくつかの注意:

  • これは私がそれをテストしてみた春+休止状態
  • と非完全なJEEアプリケーションサーバーに適用されますJDK 1.7と1.8では、リークの点で違いはありません。
  • 正規Apache Tomcatが(TomEEしかしある)
  • List of Java EE Compliant App Servers