4

Spring、Hibernate、および JPA を使用する Web アプリケーションで問題が発生しています。問題は、時間の経過とともに増加し、決して減少しないように見える非常に高いメモリ消費です。これらは、EntityManager の不適切な使用に起因する可能性が最も高いです。私は周りを検索しましたが、確かなものはまだ見つかりません。

唯一の EntityManager が注入される次の GenericDAO をすべて拡張する DAO を使用しています。

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

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

汎用 DAO が使用されるのは、ID などでエンティティを取得するメソッドがあり、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 へのデータの自動インポートのたびに、メモリが増加していることに気付きました (それぞれ 25,000 行の最大 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 がクリアされていないのでしょうか?

4

3 に答える 3

3

私はあなたの評価に同意する傾向があります。 EntityManagers は、実際には singleton として使用するようには設計されていません。EntityManager をフラッシュしても、メモリから何も消去されず、エンティティがデータベースと同期されるだけです。

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

確かに、長寿命の EntityManager が必要な場合もありますが、そのようなケースは比較的少なく、適切に実装するには十分な理解が必要です。

*注: OpenSessionInView では、N+1 問題を回避するために細心の注意が必要です。View の Open Session を AntiPattern と呼ぶ人がいるほど大きな問題です。注意して使用してください。

編集

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

于 2012-04-06T22:04:32.797 に答える
1

非 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();
      }
   }
} 

いくつかのメモ:

  • これは、Spring + Hibernate を使用する非フル JEE アプリケーション サーバーに適用されます。
  • JDK 1.7 と 1.8 でテストしましたが、リークに関して違いはありません。
  • 通常の Apache Tomcat は真の JEE アプリケーション サーバーではありません(ただし、 TomEEはそうです)
  • Java EE 準拠のアプリケーション サーバーのリスト
于 2017-03-15T22:02:21.343 に答える
0

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

于 2014-08-20T13:52:26.380 に答える