Служба Windows с NHibernate увеличивает используемую память

Я отлаживаю существующую службу Windows (написанную на C#), которую необходимо перезапускать вручную каждые несколько месяцев, потому что она продолжает потреблять память.

Сервис не очень сложный. Он запрашивает файл json с внешнего сервера, на котором хранятся продукты. Затем он разбирает этот файл JSON в список продуктов. Для каждого из этих продуктов проверяется, существует ли этот продукт в базе данных. Если нет, то он будет добавлен, если он существует, свойства будут обновлены.

База данных является базой данных PostgreSQL, и мы используем NHibernate v3.2.0 в качестве ORM.

Я использовал JetBrains DotMemory для профилирования службы, когда она работает: DotMemory контролирует

Служба запускается и через 30 с начинает работать. SnapShot # 1 производится до первого запуска. Снимок № 6 был сделан после 5-го запуска. Другие снимки также сделаны после пробега. Как вы можете видеть после каждого запуска количество объектов увеличивается с прибл. 60 КБ и используемая память увеличивается на несколько МБ после каждого запуска.

При более внимательном рассмотрении снимка № 6 видно, что оставшийся размер в основном используется объектами сеанса NHibernate:

Снимок № 6

Вот мой код 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 МБ.,

Другие вопросы по тегам