我正在调试现有的Windows服务(用C#编写),需要每隔几个月手动重启一次,因为它会继续占用内存。
服务不是很复杂。它从外部服务器请求一个json文件,该服务器保存产品。 接下来,它将此json文件解析为产品列表。 对于这些产品中的每一个,它都在检查该产品是否已存在于数据库中。如果不存在,如果它存在则将被添加,属性将被更新。
数据库是PostgreSQL数据库,我们使用NHibernate v3.2.0作为ORM。
我一直在使用JetBrains DotMemory在服务运行时对其进行分析:
服务开始,30年后它开始工作。 SnapShot#1是在第一次运行之前制作的。 快照#6是在第5次运行后制作的。 其他快照也在运行后生成。 正如您在每次运行后看到的那样,对象的数量会增加大约。每次运行后,60k和使用的内存会增加几MB。
仔细观察Snapshot#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中,我使用两个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);
}
和EndSession
:
ISession session = ThreadLocalSessionContext.Unbind(_sessionFactory);
if (session != null)
{
session.Close();
}
在我的数据访问类中,我在开始时调用base.BeginSession
,然后在base.EndSession
调用。
答案 0 :(得分:0)
关于Singleton的建议让我仔细研究了我的数据访问类。
我认为在每次运行时创建这个类会在NHibernate内存超出范围时释放它。我甚至在类的析构函数中添加了一些dispose调用。但这不起作用,或者更可能是我没有正确地做到这一点。 我现在将我的数据访问类保存在静态字段中并重新使用它。现在我的记忆力不再增加,更重要的是打开物体的数量保持不变。我再次使用DotMemory运行服务超过一个小时调用运行150次左右,最后一个快照的内存仍然是105MB左右,对象的数量仍然是117k而我的SessionFactory字典现在只有4MB而不是150 * 4MB