Я пытаюсь интегрировать Hibernate Search в один из проектов, над которыми я сейчас работаю. Первый шаг в этом начинании довольно прост - проиндексировать все существующие объекты с помощью Hibernate Search (который использует Lucene под капотом). Многие из таблиц, сопоставленных с сущностями в доменной модели, содержат много записей (> 1 миллиона), и я использую простую технику разбиения на страницы, чтобы разделить их на более мелкие единицы. Однако я испытываю некоторую утечку памяти при индексации объектов. Вот мой код:
@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 которого я выполняю вручную через консоль JMoss JBoss. Когда я наблюдаю за выполнением метода в JVisualVM, я вижу, что использование памяти постоянно растет, пока не будет израсходована вся куча, и хотя происходит много сборок мусора, никакая память не освобождается, что заставляет меня поверить, что я ввел утечку памяти в моем код. Однако я не могу обнаружить код, вызывающий нарушение, поэтому надеюсь на вашу помощь в его поиске.
Проблема, конечно же, не в самой индексации, потому что я получаю утечку даже без нее, так что я думаю, что неправильно делаю нумерацию страниц. Единственная ссылка на сущности, которые у меня есть, это список сущностей , который должен легко собираться мусором после каждой итерации цикла, вызывающего indexUnit .
Заранее спасибо за помощь.
EDIT
Изменение кода на
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 есть какая-то ошибка: хранить ссылки не следует, но кто знает.