代码之家  ›  专栏  ›  技术社区  ›  Paul Meems

使用NHibernate的Windows服务正在增加使用的内存

  •  1
  • Paul Meems  · 技术社区  · 7 年前

    我正在调试一个现有的windows服务(用C语言编写),它需要每隔几个月手动重启一次,因为它一直在消耗内存。

    接下来,它将这个json文件解析为产品列表。 对于每种产品,它都会检查数据库中是否已经存在该产品。如果不存在,则会添加。如果存在,则会更新属性。

    该数据库是一个PostgreSQL数据库,我们使用NHibernate v3.2.0作为ORM。

    我一直在使用JetBrains DotMemory在服务运行时对其进行评测: DotMemory overvies

    服务开始,30秒后开始工作。快照#1在第一次运行之前创建。 快照#6是在第5次运行后制作的。 其他快照也在运行后生成。 正如您可以看到的那样,每次运行后,对象的数量增加了约60k,使用的内存在每次运行后增加了几MB。

    仔细观察快照#6,可以发现保留的大小主要由NHibernate会话对象使用:

    Snapshot #6

    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);
    }
    

    我的DoService工作:

    try
    {
        // Call execute
        var processor = new SAPProductProcessor();
        processor.Execute();
    }
    catch (Exception ex)
    {
        _logger.Error("Error in DoServiceWork", ex);
    }
    finally
    {
        // Next round:
        _timer.Start();
    }
    

    在SAPProductProcessor中,我使用两个db调用。两者都在一个循环中。 我循环遍历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);
    }
    

    和a EndSession :

    ISession session = ThreadLocalSessionContext.Unbind(_sessionFactory);
    if (session != null)
    {
        session.Close();
    }
    

    在我的数据访问类中,我调用 base.BeginSession 在开始和 base.EndSession 然后结束。

    1 回复  |  直到 7 年前
        1
  •  0
  •   Paul Meems    7 年前

    关于单例的建议让我仔细研究了我的数据访问类。

    我认为每次运行创建这个类时,都会在超出范围时释放NHibernate内存。我甚至在类的析构函数中添加了一些dispose调用。但这不起作用,或者更可能的是我做得不对。 现在,我将数据访问类保存在静态字段中,并重新使用它。现在我的内存不再增加,更重要的是,打开的对象的数量保持不变。我只是使用DotMemory再次运行服务一个多小时,调用run大约150次,最后一个快照的内存仍然在105MB左右,对象数仍然是117k,我的SessionFactory字典现在只有4MB,而不是150*4MB。