Windows NHibernate 服务正在增加已用内存
Windows service with NHibernate is increasing used memory
我正在调试现有的 windows 服务(用 C# 编写),该服务需要每隔几个月手动重启一次,因为它一直在消耗内存。
服务不是很复杂。它从保存产品的外部服务器请求 json 文件。
接下来它将此 json 文件解析为产品列表。
对于这些产品中的每一个,它都会检查该产品是否已存在于数据库中。如果不存在,将添加它,如果它存在,将更新属性。
数据库是PostgreSQL数据库,我们使用NHibernate v3.2.0作为ORM。
我一直在使用 JetBrains DotMemory 在 运行s 时分析服务:
服务启动并在 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 文件中的所有产品,并使用产品代码检查产品是否已经在 table 中:
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())
但这并没有阻止内存的增加。
更新
在我的数据访问的构造函数中 class MultiSessionFactoryProvider sessionFactoryProvider
被注入。基 class 用 : base(sessionFactoryProvider.GetFactory("data"))
调用。这个基础 class 有一个方法 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();
}
在我的数据访问中 class 我在开始时调用 base.BeginSession
,然后在结束时调用 base.EndSession
。
关于 Singleton 的建议让我更仔细地查看了我的数据访问 class。
我认为在使用每个 运行 创建此 class 时,当它 运行 超出范围时会释放 NHibernate 内存。我什至在 class' 的析构函数中添加了一些处置调用。但这没有用,或者更可能是我没有正确地做。
我现在将我的数据访问 class 保存在静态字段中并重新使用它。现在我的内存不再增加,更重要的是打开对象的数量保持不变。我刚刚 运行 服务再次使用 DotMemory 一个多小时,调用 运行 大约 150 次,最后一个快照的内存仍然在 105MB 左右,对象数量仍然是 117k,我的 SessionFactory 字典是现在只有 4MB 而不是 150*4MB。
我正在调试现有的 windows 服务(用 C# 编写),该服务需要每隔几个月手动重启一次,因为它一直在消耗内存。
服务不是很复杂。它从保存产品的外部服务器请求 json 文件。 接下来它将此 json 文件解析为产品列表。 对于这些产品中的每一个,它都会检查该产品是否已存在于数据库中。如果不存在,将添加它,如果它存在,将更新属性。
数据库是PostgreSQL数据库,我们使用NHibernate v3.2.0作为ORM。
我一直在使用 JetBrains DotMemory 在 运行s 时分析服务:
服务启动并在 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 文件中的所有产品,并使用产品代码检查产品是否已经在 table 中:
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())
但这并没有阻止内存的增加。
更新
在我的数据访问的构造函数中 class MultiSessionFactoryProvider sessionFactoryProvider
被注入。基 class 用 : base(sessionFactoryProvider.GetFactory("data"))
调用。这个基础 class 有一个方法 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();
}
在我的数据访问中 class 我在开始时调用 base.BeginSession
,然后在结束时调用 base.EndSession
。
关于 Singleton 的建议让我更仔细地查看了我的数据访问 class。
我认为在使用每个 运行 创建此 class 时,当它 运行 超出范围时会释放 NHibernate 内存。我什至在 class' 的析构函数中添加了一些处置调用。但这没有用,或者更可能是我没有正确地做。 我现在将我的数据访问 class 保存在静态字段中并重新使用它。现在我的内存不再增加,更重要的是打开对象的数量保持不变。我刚刚 运行 服务再次使用 DotMemory 一个多小时,调用 运行 大约 150 次,最后一个快照的内存仍然在 105MB 左右,对象数量仍然是 117k,我的 SessionFactory 字典是现在只有 4MB 而不是 150*4MB。