我有一个 asp.net 网站托管在IIS中。我最近注意到,随着从数据库返回大型结果集,IIS 工作进程的内存不断增长(每次运行查询时大约 400MB(。如果这些大型查询中的一些碰巧同时运行,它只会消耗内存(已经看到它达到5GB(,服务器会立即变慢。
当数据加载到数据表中时,我已将其缩小为一行代码。
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(storedProcedureName, connection))
using(DataTable dataTable = new DataTable())
{
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
// Memory Spikes on dataTable.Load
dataTable.Load(reader);
}
}
我不明白的是,分配给数据表的内存似乎没有像我预期的那样被处理掉。当 DataTable 超出范围时,或者当网页被导航离开时,甚至当用户注销网站时,内存将保持在同一级别。这显然是多用户系统中的一个问题。
我使用了一个内存探查器,它在内存中保存了数千个字符串,这些字符串是 DataTable 中保存的查询的结果,但我不确定从这里开始?我是否误解了我应该如何处理这个问题?
这不是问题。这就是垃圾收集器的工作方式。 当您释放一个对象时,它不会立即从内存中删除。它只是标记为准备处理垃圾回收器。
这是MS考试书中的一句话
堆栈在方法结束时自动清除。CLR 会处理这个问题,您不必担心。堆是另一回事 — 它由垃圾回收器管理。在没有垃圾回收器的非托管环境中,您必须跟踪在堆上分配了哪些对象,并且需要显式释放它们。在.NET Framework中,这是由垃圾回收器完成的。
垃圾回收器使用标记和紧凑算法。集合的标记阶段检查堆上的哪些项仍被根项引用。根可以是静态字段、方法参数、局部变量或 CPU 寄存器。如果垃圾回收器在堆上找到"活动"项,则会标记该项。检查整个堆后,压缩操作开始。然后,垃圾回收器将所有活动堆对象靠近移动,并为所有其他对象释放内存。为此,垃圾回收器必须确保在执行所有标记和压缩时没有状态更改。因此,在执行收集操作时,所有线程都会被冻结。它还必须确保所有对生物对象的引用都是正确的。移动对象后,垃圾回收器将修复对对象的所有现有引用。
您可以尝试使用以下方法强制垃圾回收器执行清理:
GC.Collect();
GC.WaitForPendingFinalizers();
此外,GC 使用多代,只有第 0 代易于清理,GC 首先清理它。直到那时,GC 才决定它无法释放足够的内存,它将开始处理其他世代。转移到这些世代可能会造成延迟。
更新:您也可以尝试将大数据集划分为小块并相应地检索它们。