Hibernate, SessionFactoryObjectFactory и OutOfMemoryError:пространство кучи java

Там, где я работаю, мы сталкиваемся с проблемами, связанными с тем, что JVM не хватает пространства кучи в одном из наших приложений. Я немного поискал причину этого, в том числе просмотрел дамп кучи с помощью профилировщика, но теперь я в значительной степени застрял.

Во-первых, немного о рассматриваемой системе: это Java-приложение, использующее Spring и Hibernate для ведения записей об организациях. Система состоит из набора клиентов веб-сервиса, который используется для получения данных об организациях от государственного учреждения, отвечающего за этот тип данных. Кроме того, система ведет локальную базу данных с такими данными, выступающую в роли кэша для вызовов веб-сервиса, так что при первом запросе информации об организации она сохраняется в локальной реляционной базе данных и используется для поиска. данных для следующих запросов. Hibernate используется для связи с этой базой данных.

Проблема, как указывалось ранее, заключается в том, что через некоторое время приложение начинает аварийно завершать работу с OutOfMemoryError: java heap space. Я просмотрел дамп кучи с помощью Eclipse+MAT и определил, что виновником является Hibernate SessionFactoryObjectFactory, занимающий примерно 85% выделенной памяти (вся оставшаяся память).Мне было немного сложно точно определить, какой тип объектов хранится в нем. На верхнем уровне находится Glassfish WebappClassLoader, который содержит org.hibernate.impl.SessionFactoryObjectFactory. В нем содержится org.hibernate.util.FastHashMap, который, в свою очередь, содержит java.util.HashMap. Он содержит ряд записей, каждая из которых содержит запись HashMap, org.hibernate.impl.SessionFactoryImplи строку. HashMap-entry, в свою очередь, содержит те же три объекта: HashMap-entry, SessionFactoryImplи String, и эта структура повторяется несколько раз. SessionFactoryImplсодержит ряд объектов, в первую очередь org.hibernate.persister.entity.SingleTableEntityPersister, который содержит ряд строк и HashMaps. Некоторые строки ссылаются на переменные в объектах предметной области, а некоторые содержат sql-операторы.

На первый взгляд казалось, что этот объект занимает лишнее количество памяти (файл дампа был 800 МБ, из которых 650 МБ было занято SessionFactoryObjectFactory), поэтому я включил ведение журнала загрузки и выгрузки объекта и попытался запросить у системы данные об организации (через вызов веб-сервиса из другой системы). Что я заметил здесь, так это то, что было много сообщений о загрузке объектов, но очень мало о выгруженных объектах (единственные, которые были, это выгрузка библиотечных объектов).Это привело меня к мысли, что однажды загруженный в память объект (скажем, организация) никогда не выгружается, а это означает, что со временем в системе закончится память. (Справедливо ли это предположение, основанное на том, что было найдено в журнале?)

Затем я попытался найти причину этого, но это оказалось намного сложнее. Поскольку объекты, загруженные Hibernate, будут жить до тех пор, пока существует их сеанс, я попытался изменить способ обработки сеансов, заменив вызовы Spring HibernateDaoSupport#getSession()на HibernateDaoSupport#getSessionFactory(). getCurrentSession(). Это никак не повлияло на проблему. Я также попытался добавить вызовы ... getCurrentSession().flush()и .clear()в блок finally некоторых рассматриваемых методов Dao, а также с нет внешнего эффекта. (Все дао-методы аннотированы @Transactional, что должно означать, что сеанс должен быть активен только в рамках @Transactional-метода, а последовательные вызовы метода должны быть разными. сеансы при вызове getCurrentSession()(?))

Итак, теперь я в значительной степени застрял, когда дело доходит до поиска других областей для проверки. У кого-нибудь есть идея или какой-то указатель о том, где искать и что искать?

Дамп кучи показал, что существует много экземпляров org.hibernate.impl.SessionFactoryImpl. Это так, как ожидалось? (Я бы подумал, что должен быть только один экземпляр SessionFactory или несколько вершин.)

Редактировать:

Думаю, мне действительно удалось решить проблему:

Оказалось, что проблема в том, как обрабатывались зависимости от других объектов в классах веб-сервисов. Это было решено путем вызова new ClassPathXmlApplicationContext(...)в конструкторе классов веб-сервиса. Это привело к загрузке большого количества объектов для каждого запроса (или, по крайней мере, каждый сеанс), которые не были выгружены снова (в основном Hibernate SessionFactoryImpl). Я изменил классы веб-сервисов, чтобы они вместо этого внедряли свою зависимость и формировали то, что я видел, используя профилировщики до сих пор, проблема с несколькими объектами SessionFactoryImplбыла решена.

Я думаю, что проблема могла усугубиться при обновлении с GlassFish 2.x до GlassFish 3.x, возможно, есть некоторые различия в том, как создаются экземпляры классов веб-сервисов.

7
задан Tobb 23 March 2018 в 18:38
поделиться