8

アプリケーションの新しいバージョンで毎日 OutOfMemory エラーが発生します。Tomcat には 1.5 GB のヒープが割り当てられています。

Eclipse メモリ アナライザー ( http://www.eclipse.org/mat/ ) を使用すると、Shortest Accumulation Path の下に次の情報が表示されます。

org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList$Listable @ 0xa1566cc8  
    _head org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList @ 0xa1566ca8  
      _pool org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool @ 0xa1566c38 
         connectionPool org.apache.tomcat.dbcp.dbcp.BasicDataSource @ 0xa1566980 
             dataSource org.springframework.orm.jpa.JpaTransactionManager @ 0xa0b01760 
                   <Java Local> java.lang.Thread @ 0xa4005900 ajp-8141-5 Thread 

これをさらに調べると、Hibernate クエリである多くの重複文字列が示されます。アプリケーションのホーム画面で、ドキュメントのリストを読み込みます。クエリの重複は、ヒープ ダンプに 8,241 回存在します。

また、1 GB のヒープが org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool に含まれていることにも気付きました。このドキュメント クエリは、ドキュメントのバイナリ データを読み込んでいました。読み込んでいるドキュメントは約 1MB です。これは、List がガベージ コレクターによってクリーンアップされていないのではないかと疑っています。クエリから不要なデータを削除しますが、それでもオブジェクトが残っていることが気になります。

JPA、Hibernate、Spring を使用しています。@Transactional(readOnly=true)ドキュメントリストを取得するメソッドで使用 しています。データ ソースの Spring 構成は次のとおりです。

<jee:jndi-lookup jndi-name="jdbc/MyDB" id="myDataSource"/>

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="dataSource" ref="myDataSource"/>
        <property name="persistenceUnitName" value="WebPU"/>
        <property name="persistenceProvider">
            <bean class="org.hibernate.ejb.HibernatePersistence" />
        </property>
        <property name="jpaVendorAdapter">
            <bean
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="SQL_SERVER" />
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
            </bean>
        </property>

    </bean>

     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="myEmf" />
    </bean>

Tomcat を使用して接続プーリングを提供しています。これが私の構成です:

<Resource auth="Container" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" initialSize="20"
  logAbandoned="true" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/MyDB" password="pass" poolPreparedStatements="true" removeAbandoned="true" removeAbandonedTimeout="30" 
  type="javax.sql.DataSource" 
  url="jdbc:sqlserver://devmssql;databaseName=MY_DEV;responseBuffering=adaptive;port=1444" 
  username="user"/>

サービス層には @Transactional があります。私のJPAクエリは次のようになります。

public List<Document> getDocs(int cId, int lId, int ldId) { 
        CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Document> select = queryBuilder.createQuery(Document.class);

        Root<Document> from = select.from(Document.class);

        select.where(
                queryBuilder.and(queryBuilder.equal(from.get(Document_.cId), cId),
                queryBuilder.equal(from.get(Document_.lId), lId),
                queryBuilder.equal(from.get(Document_.ldId), ldId)));




        TypedQuery<Document> tq = getEntityManager().createQuery(select);

        final List<Document> rl = tq.getResultList();

        return rl;
    }

メモリ リークの根本原因を特定するには、何を調べればよいですか? これに寄与する可能性のあるDBCP、Hibernate、またはSpringの設定はありますか? JPA クエリ コードで、貢献している可能性があることに気付いた点はありますか?

4

2 に答える 2

1

VisualVMまたはjProfilerを使用してリークを特定することを強くお勧めします。その私見は最も簡単な方法です。

于 2012-05-09T17:23:33.607 に答える
0

メソッドから戻る前に、EntityManager キャッシュをクリアしてみてください。最初に EntityManager.flush() メソッドを呼び出し、次に clean() メソッドを呼び出します。これは、EntityManager が長く「存続」し、多くのデータベース操作で使用される場合に役立ちます。

Spring を使用する場合は、問題のある状況で Hibernate の使用をあきらめて、プレーンな JDBC を使用できます。Spring はクエリを簡単に操作するための JDBCTemplate を提供し、共通のトランザクション管理インターフェイスも提供するため、Hibernate 操作と JDBC クエリを混在させることができます。

于 2012-04-17T19:36:20.933 に答える