解决 (System.OutOfMemoryException) 内存ASP.NET

本文可帮助 你排查 内存不足ASP.NET。

原始产品版本:   ASP.NET
原始 KB 编号:   2020006

症状

我们在 Microsoft 客户支持服务中看到的最常见问题之一是 OutOfMemoryException 方案。 因此,我们将一组资源放在一起,以帮助排查和确定内存问题的原因。

在介绍疑难解答的详细信息之前,了解 OutOfMemoryException 导致此问题的原因非常重要。 与许多开发人员认为的相反,已安装的 RAM 量不会影响安装 RAM 的可能性 OutOfMemoryException 。 32 位操作系统可以处理 4 GB 的虚拟地址空间,无论框中安装的物理内存量如何。 其中,为操作系统保留 2 GB (内核模式内存) 2 GB 分配给用户模式进程。 分配给内核模式内存的 2 GB 在所有进程之间共享,但每个进程获取自己的 2 GB 用户模式地址空间。 这一切都假定你未在启用开关 /3gb 时运行。

当应用程序需要使用内存时,它将保留一个虚拟地址空间块,然后从该区块提交内存。 这正好是.NET Framework垃圾回收器 (GC) 内存来增大托管堆时所完成。 当 GC 需要小型对象堆的新段 (小于 85 KB 的对象驻留) ,则分配 64 MB。 当大型对象堆需要一个新段时,它将分配 16 MB。 必须从进程必须使用的 2 GB 地址空间的连续块满足这些大型分配。 如果操作系统无法满足 GC 对连续内存块的请求, (System.OutOfMemoryException OOM) 发生。

备注

在 64 位操作系统上运行的 32 位进程可以处理 4 GB 的用户模式内存,在 64 位操作系统上运行的 64 位进程可以处理 8TB 的用户模式内存,因此 64 位操作系统上的 OOM 不太可能。 在 64 位操作系统上运行的 32 位进程中,可以体验 OOM,但此过程通常不会发生,直到使用接近 3 GB 的专用字节。

您可能会看到 OOM 条件的原因有两个。

  1. 在 32 位环境中, (占用的内存通常超过 800 MB。)
  2. 虚拟地址空间是零碎的,从而减少大型连续分配成功的可能性。

也可以查看 OOM 条件,因为 1 和 2 的组合。 For more information, see Troubleshooting System.OutOfMemoryExceptions in ASP.NET.

发生 OOM 时,您可能会注意到以下一种或多个症状:

  • 应用程序崩溃。 有关详细信息,请参阅这个 OutOfMemory小人是谁,我有足够的内存时,他为什么要使进程崩溃? 。

  • 您的应用程序可能会遇到任务管理器或性能监视器指示的高内存。

  • 处理请求可能需要很长时间。

    在 Internet Information Services (IIS) 7 上,可以使用 IIS 7 中的跟踪功能对失败请求进行故障排除,以对长时间运行的请求进行故障排除。

  • 由于 OOM,用户可能会报告应用程序中的错误消息。

在确定 OOM 条件的原因时,实际上是在努力确定高内存情况或零碎地址空间的原因。 尽管无法记录这些情况的所有可能原因,但我们定期看到一些常见原因。

以下信息概述了 OOM 条件的常见原因以及解决其中每个原因的解决方法。

字符串连接

托管应用程序中的字符串 (使用托管应用程序编写的应用程序.NET Framework) 不可变。 向字符串分配新值时,将对现有字符串创建副本。 并将新值分配给新字符串。 它通常不会导致任何问题。 但是,当大量字符串连接在一起时,最终会导致比开发人员可能意识到更多的字符串分配。 它可能会导致内存增长和 OOM 条件。

若要避免由于字符串连接而使用 OOM,请确保使用 StringBuilder 类。 有关详细信息,请参阅如何在Visual C# 中提高字符串连接C#。

托管堆中的碎片

托管应用程序中 (GC) 会压缩堆以减少碎片量。 但是,可以固定托管应用程序中的对象。 堆压缩期间不能移动固定的对象。 这样做将更改对象所在的地址。 如果应用程序固定大量对象,并且/或长时间固定对象,则可能会导致托管堆中出现碎片。 它可能会导致 GC 更频繁地增大托管堆并导致 OOM 条件。

自 1.0 起,我们一直在通过固定来最大程度地减少 OOM .NET Framework情况。 在每个版本中都进行了增量改进。 但是,你仍然可以实现一些设计模式,如果你需要固定对象,这些模式将非常有益。

VA 空间中虚拟地址 (碎片)

每个进程都有一定数量的内存分配给它,并且该内存表示进程的 VA 空间。 如果 VA 空间变得零碎,则会增加 GC 无法获取大量连续内存来增大托管堆的可能性。 它可能会导致 OOM 条件。

VA 空间中的碎片通常是由以下一个或多个方案导致的:

  • 将同一程序集加载到多个应用程序域中。

    如果需要在同一个应用程序中运行的多个应用程序中使用程序集应用程序池,请强命名该程序集,并安装到 GAC 中。 通过执行此操作,可以确保程序集仅加载到进程中一次。

  • 在生产中运行应用程序,同时将元素的调试 <compilation> 属性设置为 true

  • 在可加速样式表语言中使用脚本 (XSL) 转换或创建 XmlSerializers

    在这种情况下,由可扩展样式表语言转换引起的动态程序集 (XSLT) 脚本或 XmlSerializers

返回大型数据集

使用来自数据库或其他数据源的数据时,限制返回的数据量非常重要。 例如,缓存返回整个数据库表的查询结果以避免根据需要从数据库中检索部分数据的成本不是一个好方法。 这样做很容易导致内存高,并会导致 OOM 条件。 允许用户启动类似的查询是创建高内存情况的另一种常见方法。 例如,返回公司中的所有员工或德克萨斯州的所有客户,其姓氏以字母 S 开头。

始终限制可以从数据库返回的数据量。 不允许查询,例如, SELECT * FROM. . . 您随后无法控制在页面中显示的数据数。

确保不在 UI 元素(如 GridView 控件)中显示大型数据结果同样重要。 除了返回的数据所需的内存之外,还将使用字符串和 UI 元素中呈现结果所需的大量数据。 通过实现分页和验证输入,以便不返回大型数据集,可以避免此问题。

在启用跟踪的生产环境中运行

ASP.NET跟踪是一项功能强大的应用程序疑难解答功能。 但绝不应在生产环境中继续工作。 ASP.NET跟踪使用数据结构(如存储跟踪信息)。随着时间的推移,它们可能导致高内存条件,导致 DataTables OOM。

应在生产环境中禁用跟踪。 为此,可以在自定义文件中将元素web.configenabled <trace> false。 使用启用零售部署 <deploy retail="true" /> 也会禁用应用程序中的跟踪。

泄露本机资源

许多托管资源也将使用本机资源。 由于 GC 不清理本机资源,开发人员负责实现和调用 Dispose 方法以清理本机资源。 如果使用实现接口的类型,并且未调用该方法,则有泄露本机资源并导致 IDisposable Dispose OOM 条件的风险。

这些对象应实现接口,并且应在不再需要这些对象时 iDisposable Dispose 调用该方法。