我正在调试一个需要每隔几个月手动重新启动的现有 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
结束时调用。