java - JBoss AS 5.1 下分页 JPA 查询的内存泄漏

标签 java hibernate jpa jboss5.x hibernate-search

我正在尝试将 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 中方法的执行时,我发现内存使用量不断增长,直到所有堆都被消耗,尽管发生了很多垃圾收集,但没有内存被释放,这让我相信我在我的程序中引入了内存泄漏。代码。然而,我无法发现有问题的代码,所以我希望您能帮助找到它。

问题肯定不在于索引本身,因为即使没有索引我也会发生泄漏,所以我认为我没有正确执行分页。然而,对我拥有的实体的唯一引用是列表entities,在调用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 查询本身有问题,保留了不应该保留的引用,但谁知道呢。

最佳答案

看起来注入(inject)的 EntityManager 保留了对从查询返回的所有实体的引用。它是一个容器管理的 EM,因此应该在事务结束时自动关闭或清除 - 但您正在执行一堆非事务性查询。

如果您只想对实体建立索引,您可能需要在 init() 中的循环末尾调用 em.clear()。这些实体将被分离(EntityManager 跟踪对它们所做的更改),但如果它们只是要被 GC 处理,那应该不是问题。

关于java - JBoss AS 5.1 下分页 JPA 查询的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3253627/

相关文章:

java - 在 Websphere WebApp 中将 JUL 重定向到 Log4J2

java - JPA 中的函数参数与 JAXB 类型不兼容

java - 如何捕获 java.sql.SQLIntegrityConstraintViolationException?

java - 使用 Hibernate 排除值

java - 使用 N :1 relationship 将同一列保存在 JPA secondaryTable 中

mysql - 使用 Spring、Hibernate 和 mySQL 进行事务管理

rest - 错误: Getters of lazy classes cannot be final Kotlin Spring Boot

java - 如何测量由单个字符串呈现并受宽度限制的文本的高度,以便它是多行的?

java - 了解正常运行时间计时器是否已重置或 Android 是否已重新启动

Java路径末尾的两个点有意想不到的结果