【问题标题】:ASP.NET Memory Management TechniquesASP.NET 内存管理技术
【发布时间】:2010-05-27 19:27:29
【问题描述】:

我们有几个使用 ASP.NET 和 DevExpress ASPxGridView 组件的不同项目。在这些项目的整个开发过程中,已经使用了几种数据绑定技术,我们现在发现其中一些项目正在占用服务器上的所有内存。

最初,我们使用对存储过程的调用并将 DataSet 绑定到 gridview,但根据 DX 建议,将其修改为 ObjectDataSource 并创建最终使用 Linq 语句针对 DB 并返回泛型的对象然后绑定的对象列表。

不幸的是,这并不能解决当前的问题。我们仍然注意到大量的内存被吃掉了,我正试图弄清楚这一点。通过 RedGate 内存分析器运行时,我注意到每次重新绑定到网格时都会创建很多字符串、RuntimeTypeHandles 和我的对象实例。

DataBind 在页面加载时完成,网格在排序时使用回发,但这会导致 MB 的内存在每次绑定时泄漏,所以我想知道我可以使用哪些技术/最佳实践来管理我们的对象有控制权吗?我已经在数据对象中实现了 IDisposable,处理了 linq 上下文并将任何其他对象设置为 null,但这似乎没有什么不同。我似乎在每次调用时都创建了一个数据对象的实例,甚至调用 dispose 也没有什么区别。

【问题讨论】:

  • 请描述“泄漏”。这些对象是否仍然可以通过某种方式到达?否则,这不是泄漏的情况,而是(过多)分配和 GC 上的负载。您是否分析了“# GC 集合”?
  • 兆字节?那是一些网格...您是否正确使用分页?使用LinqDataSource 应该很容易
  • 您是否遇到内存不足异常或其他类型的内存问题?如果您将页面运行一百或一千次,内存最终会耗尽,还是会发生“坏事”?换句话说,内存使用真的有害还是正常?我使用 ASPxGridViews 并没有这个问题。
  • 我仍在学习如何使用 Redgate 工具,所以我不能 100% 确定我所看到的是“泄漏”。我运行网页,让它加载。然后我拍了一张。我点击一列进行排序。我让它完成,然后拍了一张快照。快照比较显示,在快照 1 和 2 之间,我创建了 2 个额外的对象(我的 ObjectDataSource 附加到的对象)。当前活动实例显示为 3。我再次点击该列,当前活动实例显示为 4。有一个 Disposing 方法,每次都在 ObjectDataSource 上被命中,我在其中调用 Dispose。
  • 实际问题是,当这个软件在实时服务器上运行时,它会占用内存,导致 OutOfMemory 异常。我查看了 LinqDataSource,但不知道如何将它绑定到除了单个 Linq 表之外的任何东西。

标签: c# asp.net memory-management


【解决方案1】:

哇,里面有很多管道和活动部件。

是否可以缩小范围?也就是说,你能把页面上的东西去掉,看看它是如何执行的吗?

请原谅,但是当您说“泄漏内存”时,您是什么意思?您怎么知道? GC 是“懒惰的”,除非有压力,否则不会做任何事情。这是一件好事,但它也意味着内存可能会在需要收集之前出现累积,然后您可能会发现它释放了很多。由于这个原因,内存分析器通常看起来像锯齿。

您如何存储网格数据以使分页工作?我已经看到数据集保留在视图状态中,这意味着数据与网格一起进入客户端。如果您在回发页面加载时再次查询,您将在那里浪费大量空间。

另一个常见问题是事件订阅使大对象的存活时间超过了它们应有的时间。我实际上已经看到了将数据网格置于会话状态的代码,只要会话存在,它就会使页面保持活动状态。在每次回传中,这种情况一次又一次地发生,直到噗。在这种情况下,GC 无法帮助我们,因为这些对象确实仍在“使用中”。

因此尝试简化 - 关闭排序、摆脱第 3 方控制、使用较小的数据集等。使用内存分析器和使服务器承受压力的东西,测量这种情况。如果您没有发现“泄漏”,则开始添加内容以查看何时出现问题。

【讨论】:

  • 内存泄漏是指在每次后续页面加载时创建的新对象,其中先前创建的实例尚未清理。我正在使用 Redgate Memory Profiler,我加载页面并拍摄快照作为基线。然后我进行排序,我看到创建的数据对象的 2 个新实例,因此它显示了 3 个活动实例。点击另一列进行排序,我得到了第四个实例。我们没有将网格存储在会话或视图状态中,我们只是在页面加载时绑定到网格。
【解决方案2】:

您可能每次都向 iis 服务器返回过多数据。请记住,在 devexpress 网格中使用标准 linq 数据源时,每次为排序、分页或任何其他回调执行回调时,整个数据都会加载到内存中,然后进行排序和分页。

这意味着如果您要加载大量数据,您将很容易浪费服务器内存。想想你可能有很多用户打开同一个页面,这会将每个用户的整个数据加载到内存中,而 GC 可能没有足够的时间来释放所有这些东西。

DevExpress 为此提供了LinqServerModeDataSource,它负责数据服务器中的所有分页和排序。

如果您不能使用它,请尝试通过过滤来检索较小的数据集。

【讨论】:

  • LinqServerModeDataSource 虽然需要 Linq 上下文和表名,但如果我的对象直接从 Linq 返回 IQueryable,这是否允许我使用我的数据?还是我一直在尝试对发送到 ObjectDataSource 的列表进行分页?我们某些系统上的数据需要以网格形式显示,记录范围在 100,000 多个,所以我当然可以理解为什么内存消失得如此之快