【问题标题】:Windows service with NHibernate is increasing used memoryNHibernate 的 Windows 服务正在增加使用的内存
【发布时间】:2017-11-15 13:06:32
【问题描述】:

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

服务不是很复杂。它从保存产品的外部服务器请求一个 json 文件。 接下来,它将这个 json 文件解析为产品列表。 对于这些产品中的每一个,它正在检查该产品是否已经存在于数据库中。如果不存在,则将添加它,如果它确实存在,则将更新属性。

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

我一直在使用 JetBrains DotMemory 在服务运行时对其进行分析:

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

仔细查看快照 #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

【问题讨论】:

  • 您的DoServiceWork 是单身人士吗?我看到SAPProductProcessor 每次都会被创建。也许这值得一看?
  • SessionFactory 是一个属性吗? SessionFactory 是在哪里创建的?它应该只为一个进程(或 AppDomain)创建一次。
  • 打开比较,打开所有新对象并查看持有哪些对象以及原因。
  • 感谢您的回复。我会尝试让DoServiceWork 成为单身人士。 SAPProductProcessor 最初是一个静态类。我对其进行了更改,以便可以在析构函数中进行一些清理,但这并没有帮助。我更新了我的帖子,提供了有关 SessionFactory 的更多信息。

标签: c# memory nhibernate dotmemory


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 2017-05-25
    • 2021-12-08
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    相关资源
    最近更新 更多