我正在尝试将 Hibernate Search 集成到我目前正在从事的项目之一中。这种努力的第一步相当简单 - 使用 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,我通过 JBoss 的 JMX 控制台手动执行它的init方法。当我在 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 查询本身有问题,保留不应该的引用,但谁知道呢。