1

現在取り組んでいるプロジェクトの1つにHibernateSearchを統合しようとしています。このような取り組みの最初のステップは、かなり単純です。既存のすべてのエンティティにHibernate Search(内部でLuceneを使用)でインデックスを付けます。ドメインモデルのエンティティにマップされたテーブルの多くには、多くのレコード(> 100万)が含まれており、単純なページ付け手法を使用して、それらをより小さな単位に分割しています。ただし、エンティティのインデックス作成中にメモリリークが発生しています。これが私のコードです:

@Service(objectName = "LISA-Admin:service=HibernateSearch")
@Depends({"LISA-automaticStarters:service=CronJobs", "LISA-automaticStarters:service=InstallEntityManagerToPersistenceMBean"})
public class HibernateSearchMBeanImpl implements HibernateSearchMBean {
    private static final int PAGE_SIZE = 1000;

    private static final Logger LOGGER = LoggerFactory.getLogger(HibernateSearchMBeanImpl.class);

    @PersistenceContext(unitName = "Core")
    private EntityManager em;

    @Override
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void init() {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);

        Session s = (Session) em.getDelegate();
        SessionFactory sf = s.getSessionFactory();
        Map<String, EntityPersister> classMetadata = sf.getAllClassMetadata();

        for (String key : classMetadata.keySet()) {
            LOGGER.info("Class: " + key + "\nEntity name: " + classMetadata.get(key).getEntityName());

            Class entityClass = classMetadata.get(key).getMappedClass(EntityMode.POJO);
            LOGGER.info("Class: " + entityClass.getCanonicalName());

            if (entityClass != null && entityClass.getAnnotation(Indexed.class) != null) {
                index(fullTextEntityManager, entityClass, classMetadata.get(key).getEntityName());
            }
        }
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void index(FullTextEntityManager pFullTextEntityManager, Class entityClass, String entityName) {
        LOGGER.info("Class " + entityClass.getCanonicalName() + " is indexed by hibernate search");

        int currentResult = 0;

        Query tQuery = em.createQuery("select c from " + entityName + " as c order by oid asc");
        tQuery.setFirstResult(currentResult);
        tQuery.setMaxResults(PAGE_SIZE);

        List entities;

        do {
            entities = tQuery.getResultList();
            indexUnit(pFullTextEntityManager, entities);

            currentResult += PAGE_SIZE;
            tQuery.setFirstResult(currentResult);
        } while (entities.size() == PAGE_SIZE);

        LOGGER.info("Finished indexing for " + entityClass.getCanonicalName() + ", current result is " + currentResult);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void indexUnit(FullTextEntityManager pFullTextEntityManager, List entities) {
        for (Object object : entities) {
            pFullTextEntityManager.index(object);
            LOGGER.info("Indexed object with id " + ((BusinessObject)object).getOid());
        }
    }
}

これは単純なMBeanであり、そのinitメソッドはJBossのJMXコンソールを介して手動で実行します。JVisualVMでメソッドの実行を監視すると、すべてのヒープが消費されるまでメモリ使用量が絶えず増加し、多くのガベージコレクションが発生してもメモリが解放されないため、メモリリークが発生したと思われます。コード。ただし、問題のあるコードを見つけることができないため、コードの特定にご協力いただければ幸いです。

問題は確かにインデックス作成自体にはありません。それがなくてもリークが発生するため、ページネーションを正しく行っていないと思います。ただし、私が持っているエンティティへの唯一の参照はリストエンティティであり、 indexUnitを呼び出すループの各反復後に簡単にガベージコレクションされるはずです。

よろしくお願いします。

編集

コードをに変更する

    List entities;

    do {
        Query tQuery = em.createQuery("select c from " + entityName + " as c order by oid asc");
        tQuery.setFirstResult(currentResult);
        tQuery.setMaxResults(PAGE_SIZE);

        entities = tQuery.getResultList();
        indexUnit(pFullTextEntityManager, entities);

        currentResult += PAGE_SIZE;
        tQuery.setFirstResult(currentResult);
    } while (entities.size() == PAGE_SIZE);

問題を軽減しました。リークはまだありますが、それほど悪くはありません。JPAクエリ自体に何か問題があると思いますが、参照を保持するべきではありませんが、誰が知っていますか。

4

3 に答える 3

0

挿入されたEntityManagerは、クエリから返されたすべてのエンティティへの参照を保持しているようです。これはコンテナ管理のEMであるため、トランザクションの終了時に自動的に閉じるかクリアする必要がありますが、トランザクション以外のクエリを大量に実行しています。

エンティティにインデックスを付けるだけの場合は、init()のループの最後でem.clear()を呼び出すことをお勧めします。エンティティは切り離されます(EntityManagerはエンティティに加えられた変更を追跡します)が、GCされるだけの場合は、問題にはなりません。

于 2010-07-15T14:16:48.123 に答える
0

この質問は本当の解決策を見つけられないようです。結局、インデックスコードを別のアプリに移動しました-リークはまだありますが、アプリは重要なコンテナの外で(巨大なヒープで)完了まで実行されているため、それほど重要ではありません。

于 2010-07-15T14:17:09.997 に答える
0

「リーク」はないと思います。ただし、永続コンテキストに多数のエンティティを蓄積していて(そうです、それらをロードしているので)、最終的にはすべてのメモリを消費していると思います。clear各ループの後にEMを実行する必要があります(clearページングがないと、ページングは​​役に立ちません)。このようなもの:

    do {
        entities = tQuery.getResultList();
        indexUnit(pFullTextEntityManager, entities);

        pFullTextEntityManager.clear(); 

        currentResult += PAGE_SIZE;
        tQuery.setFirstResult(currentResult);
    } while (entities.size() == PAGE_SIZE);
于 2010-07-15T19:32:09.887 に答える