c# - iis工作进程内存使用率与处置对象之间的关系?

标签 c# asp.net entity-framework iis

通过下面的代码,我们正在通过实体框架从Sql Server中获取大量数据

    using (var db=new Entities())
    {
        var list = db.spGetRecs().ToList();

       ///rest of the codes
    }


记录数约为400万,此后iis变得如此繁重,并且其内存使用量达到了690 mb左右,并且工作进程根本不会释放内存。
您可以简单地想到多个用户使用这样的内存量的情况,然后当然会发生out of memory exception

我要在这里提出三个问题:

1.首先,为什么iis工作进程不释放内存?

2.其次,在处理完数据后,我们如何强制iis工作进程释放内存?

3.第三,为什么尽管我处置了与此庞大数据相关的所有对象,但对iis内存使用没有任何影响?那么当与Windows进程无关时处置对象有什么意义呢?

我没有写完整的代码行,因为我不想使这个问题复杂化并使您从这个问题背后的可能具有挑战性的概念中分散出来。

顺便说一句,在我调用了垃圾收集器GC.Collect()之后,它从iis工作者进程中释放了大约20Mb。

最佳答案

即使导致该内存分配的对象已被处置,进程仍可以报告RAM的高分配。内存仍然可以被认为是保留的,但不是固定的,尽管从根本上讲,通过释放死对象深入垃圾回收工作的深度,除了跟踪无法解释的不断增长的内存使用情况外,我真的不需要担心。

第一步应该始终是最小化内存占用量,以适应​​并发请求。对于大型请求,您应该考虑使用后台进程实现请求队列,以确保以任意给定时间处理有限数量的并发请求的方式来处理这些请求,或者使用其他服务器上的资源(不这样做)影响网络服务器的响应能力。

最小化内存占用空间提示:


将包含轻量级实体定义的有限上下文用于您的流程所需的最少字段。例如,如果一个实体通常包含50+列,其中某些字段是不需要的大字符串,二进制数据等,那么具有一个限定上下文的实体定义仅引用您需要的10列即可保存记忆。
合理咬数据。利用分页在任何时候仅检索可管理的数据子集。即一次记录1000条。利用SkipTake以及OrderBy子句,然后查看返回的记录数,以评估是否还有其他页面要检索。 (而不是依赖可能昂贵的Count查询。)


后台进程是繁重的查询/处理的更好解决方案。如果它们可以在只读副本而不是主数据库上运行,那就更好了。您的Web服务器可以接收带有给定参数集的请求,而无需启动会使服务器饿死的昂贵查询,它们可以在处理队列表中简单地创建一条记录,以发信号通知后台进程来提取该记录和过程。它。该后台进程可以在完全不同的服务器或服务器场上运行。如果用户正在等待结果,则Web服务器可以定期轮询以获取后台工作人员的状态更新,并在处理完成后显示来自处理队列表或相关结果表的结果。队列可以由多个工作人员提供服务,前提是您拥有一个同步上下文来协调它们并分配工作,以避免多个工作人员承担相同的工作。

编辑:
当涉及到垃圾收集时,您会感到担心,因为进程(垃圾收集器)正在使内存处于提交状态。您期望,如果有问题的代码需要分配1GB的ram,则在处理甚至是GC.Collect()之后,内存使用量将减少1GB。没有。内存仍将显示为已提交给该进程,但仍可用于该进程的代码。您可以通过运行巨大的查询并让生成的实体/数据过期来进行测试。例如,我有一个来自另一个SO问题的最新测试数据集,其中填充了3M行数据。在系统处于相当大的负载的情况下,我尝试使用以下命令将所有3M行读入内存:

using (var context = new TestDbContext())
{
    var test = context.Messages.ToList();
    Assert.IsTrue(true);
}


根据过程监视器,这会持续一段时间,然后出现内存不足异常(大约1.7GB)。所以我将其更改为:

using (var context = new TestDbContext())
{
    var test = context.Messages.Take(1000000).ToList();
    Assert.IsTrue(true);
}


这使用了大约500MB的RAM来完成。所以我让它连续执行4次:

using (var context = new TestDbContext())
{
    var test = context.Messages.Take(1000000).ToList();
    Assert.IsTrue(true);
}
using (var context = new TestDbContext())
{
    var test = context.Messages.Take(1000000).ToList();
    Assert.IsTrue(true);
}
using (var context = new TestDbContext())
{
    var test = context.Messages.Take(1000000).ToList();
    Assert.IsTrue(true);
}
using (var context = new TestDbContext())
{
    var test = context.Messages.Take(1000000).ToList();
    Assert.IsTrue(true);
}


现在,如果最初的1M被“泄漏”,则我的应用程序进程将耗尽内存。除非没有。第一个增加到〜500MB,第二个增加到〜1.1GB,第三个增加到1.6GB。下一个开始攀升,然​​后锯齿减小至〜1GB,然后继续攀升至〜1.5GB。随后的所有读取锯齿状降低到1.4GB和1.5GB之间。我重复了6次,共10次读取。有时它的锯齿大小可低至200MB,有时几乎没有任何东西。通过交换到磁盘,不会出现内存不足异常或性能下降的情况。即使重复调用或在每个using块之后调用GC.Collect()也不明显释放内存。内存被“释放”并在以后的每次调用中重复使用。每次运行或其他读取块的性能均保持一致。

当并行运行测试的多个实例时,您会开始发现性能下降,因为可以看到每次运行都分配了尽可能多的内存,直至达到极限。所需的组合RAM超过了系统上的可用内存,因此每个内存轮流将其内存块缓存到磁盘。结果是这些测试中只有2个或3个并行运行(不同的VS实例)却导致性能显着下降,但是仍然没有内存不足的异常。

关于c# - iis工作进程内存使用率与处置对象之间的关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57947611/

相关文章:

javascript - 当用户成功/未成功注册时如何在模式弹出窗口中打开消息

c# - 起订量设置返回 null

c# - gridview使用时间戳更新更新 asp.net 时出错

c# - Entity Framework 在不同的工作站上生成不同的查询

c# - 使用 Linq to SQL 类的 Kendo 分页,查询所有数据非常慢

c# - 将对象转换为 C# 类

c# - 如何使用 C# 将每个按钮单击的名称添加到字符串生成器

c# - 即使从 OnClientClick 返回 false 后也会触发回发

.net - EF 代码首先插入多行

.NET ORM - NHibernate(或 SubSonic 或其他)与使用 Entity Framework 相比有什么优势?