Служба Windows с NHibernate увеличивает используемую память
Я отлаживаю существующую службу Windows (написанную на C#), которую необходимо перезапускать вручную каждые несколько месяцев, потому что она продолжает потреблять память.
Сервис не очень сложный. Он запрашивает файл json с внешнего сервера, на котором хранятся продукты. Затем он разбирает этот файл JSON в список продуктов. Для каждого из этих продуктов проверяется, существует ли этот продукт в базе данных. Если нет, то он будет добавлен, если он существует, свойства будут обновлены.
База данных является базой данных PostgreSQL, и мы используем NHibernate v3.2.0 в качестве ORM.
Я использовал JetBrains DotMemory для профилирования службы, когда она работает:
Служба запускается и через 30 с начинает работать. SnapShot # 1 производится до первого запуска. Снимок № 6 был сделан после 5-го запуска. Другие снимки также сделаны после пробега. Как вы можете видеть после каждого запуска количество объектов увеличивается с прибл. 60 КБ и используемая память увеличивается на несколько МБ после каждого запуска.
При более внимательном рассмотрении снимка № 6 видно, что оставшийся размер в основном используется объектами сеанса NHibernate:
Вот мой код OnStart:
try
{
// Trying to fix certificate errors:
ServicePointManager.ServerCertificateValidationCallback += delegate
{
_logger.Debug("Cert validation work around");
return true;
};
_timer = new Timer(_interval)
{
AutoReset = false // makes it fire only once, restart when work is done to prevent multiple runs
};
_timer.Elapsed += DoServiceWork;
_timer.Start();
}
catch (Exception ex)
{
_logger.Error("Exception in OnStart: " + ex.Message, ex);
}
И мой DoServiceWork:
try
{
// Call execute
var processor = new SAPProductProcessor();
processor.Execute();
}
catch (Exception ex)
{
_logger.Error("Error in DoServiceWork", ex);
}
finally
{
// Next round:
_timer.Start();
}
В SAPProductProcessor я использую два вызова БД. Оба в цикле. Я перебираю все продукты из файла JSON и проверяю, есть ли продукт в таблице, используя код продукта:
ProductDto dto;
using (var session = SessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction(IsolationLevel.ReadCommitted))
{
var criteria = session.CreateCriteria<ProductDto>();
criteria.Add(Restrictions.Eq("Code", code));
dto = criteria.UniqueResult<ProductDto>();
transaction.Commit();
}
}
return dto;
И когда productDto обновляется, я сохраняю его, используя:
using (var session = SessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction(IsolationLevel.ReadCommitted))
{
session.SaveOrUpdate(item);
transaction.Commit();
}
}
Я не уверен, как изменить код выше, чтобы перестать увеличивать память и количество объектов.
Я уже пытался использовать var session = SessionFactory.GetCurrentSession();
вместо using (var session = SessionFactory.OpenSession())
но это не остановило увеличение памяти.
Обновить
В конструкторе моего класса доступа к данным MultiSessionFactoryProvider sessionFactoryProvider
вводится И базовый класс называется с : base(sessionFactoryProvider.GetFactory("data"))
, Этот базовый класс имеет метод BeginSession
:
ISession session = _sessionFactory.GetCurrentSession();
if (session == null)
{
session = _sessionFactory.OpenSession();
ThreadLocalSessionContext.Bind(session);
}
И EndSession
:
ISession session = ThreadLocalSessionContext.Unbind(_sessionFactory);
if (session != null)
{
session.Close();
}
В моем классе доступа к данным я звоню base.BeginSession
в начале и base.EndSession
в конце
1 ответ
Предложение о Синглтоне заставило меня ближе познакомиться с моим классом доступа к данным.
Я думал, что при создании этого класса при каждом запуске освободится память NHibernate, когда он выйдет из области видимости. Я даже добавил вызов dispose в деструктор класса. Но это не сработало, или, скорее всего, я делаю это неправильно. Теперь я сохраняю свой класс доступа к данным в статическом поле и повторно использую его. Теперь моя память больше не увеличивается и, что более важно, количество открытых объектов остается неизменным. Я просто снова запускаю службу, используя DotMemory, в течение часа, вызывая запуск около 150 раз, а память последнего снимка по-прежнему составляет около 105 МБ, а число объектов по-прежнему составляет 117 КБ, а мой словарь SessionFactory теперь составляет всего 4 МБ вместо 150*4 МБ.,