【发布时间】: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