現在取り組んでいるプロジェクトの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クエリ自体に何か問題があると思いますが、参照を保持するべきではありませんが、誰が知っていますか。