1

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

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

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

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

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

仔细查看快照 #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结束时调用。

4

1 回答 1

0

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

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

于 2017-11-17T11:27:20.710 回答