Утечка памяти при использовании выгружаемых JPA-запросов под JBoss AS 5.1 - PullRequest
1 голос
/ 15 июля 2010

Я пытаюсь интегрировать 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 есть какая-то ошибка: хранить ссылки не следует, но кто знает.

Ответы [ 3 ]

0 голосов
/ 15 июля 2010

Похоже, что этот вопрос не найдет реального решения.В конце я только что переместил код индексации в отдельное приложение - утечка все еще существует, но это не имеет большого значения, так как приложение выполняется до завершения (с огромной кучей) вне критического контейнера.

0 голосов
/ 15 июля 2010

Я не думаю, что есть "утечка";однако я думаю, что вы накапливаете большое количество сущностей в контексте постоянства (да, вы загружаете их) и, в конечном счете, съедаете всю память.Вам нужно clear EM после каждого цикла (без clear, подкачка не помогает).Как то так:

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

        pFullTextEntityManager.clear(); 

        currentResult += PAGE_SIZE;
        tQuery.setFirstResult(currentResult);
    } while (entities.size() == PAGE_SIZE);
0 голосов
/ 15 июля 2010

Похоже, что введенный EntityManager удерживает ссылку на все сущности, возвращаемые по вашему запросу. Это EM, управляемая контейнером, поэтому его следует автоматически закрывать или очищать в конце транзакции, но вы выполняете кучу нетранзакционных запросов.

Если вы просто собираетесь индексировать сущности, вы можете вызвать em.clear () в конце цикла в init (). Объекты будут отсоединены (EntityManager отслеживает внесенные в них изменения), но если они просто собираются получить GC, это не должно быть проблемой.

...