【问题标题】:.net collections memory optimization - will this method work?.net 集合内存优化 - 这种方法有效吗?
【发布时间】:2011-06-17 17:17:02
【问题描述】:

就像几乎任何其他大型.NET 应用程序一样,我当前的C# 项目包含许多.net 集合。
有时我从一开始就不知道集合(List/ObservableCollection/Dictionary/等)的大小。
但是很多时候我确实知道它会是什么。

我经常收到OutOfMemoryException,有人告诉我,这不仅是因为进程大小限制,还因为碎片化。

所以我的问题是 - 每次我知道集合的预期大小时设置集合的大小(使用构造函数中的容量参数)有助于我防止至少一些碎片问题吗?

此引用来自msdn

如果集合的大小可以 估计,指定初始 容量消除了需要 执行一些调整大小 添加元素时的操作 列表。

但是,我仍然不想因为可能不是真正问题的东西而开始更改我的大部分代码。

它有没有帮助你们解决内存不足的问题?

【问题讨论】:

  • 您是否使用过内存分析器来查找罪魁祸首?
  • 内存分析能否帮助我发现集合调整大小的情况?
  • 它将帮助您找到最大和最重要的收藏

标签: c# .net performance collections


【解决方案1】:

如果您遇到 OOM,那么您可能对数据过于激进,但要回答这个问题:

是的,这可能会有所帮助一些 - 好像它必须通过加倍来不断增长集合,它最终可能会为底层数组分配和复制两倍的内存 (或更准确地说,对于被丢弃的较早的较小副本)。这些中间数组中的大部分都会被及时收集,但是当它们变大时,您将使用“大对象堆”,这很难压缩。

从正确的大小开始可以防止数组的所有中间副本。

然而,这也取决于数组中in的内容。通常,对于类,每个对象中都有更多数据(加上引用等开销)——这意味着列表不一定是内存使用的最大罪魁祸首;您可能正在消耗对象上的大部分内存。

请注意,x64 将允许更多整体空间,但数组限制为 2GB - 如果每个引用的大小加倍,则数组的最大有效长度减半。

就我个人而言,我会考虑将巨大的集合分解成更小的列表;例如,锯齿状列表。

【讨论】:

  • 关于锯齿状列表 - 在我看来,仅仅为了进行一些集合优化而更改我的代码和设计太麻烦了。
  • @Marc-Gravell - 只是要清楚......并不是说 LOH 是“更难压缩”,因为你注意到 - LOH 没有被压缩(至少从 .NET 4.7 开始)
【解决方案2】:

指定初始大小很少会解决 OutOfMemory 问题 - 除非您的集合大小是数百万个对象,在这种情况下您真的不应该保留这样的集合。

调整集合的大小涉及定义一个具有新的附加大小的全新数组,然后复制内存。如果您已经接近内存不足,是的,这可能会导致内存不足,因为无法分配新数组。

但是,100 人中有 99 人在您的应用中存在内存泄漏,并且集合大小调整问题只是其症状。

【讨论】:

  • 我选择了你回答其他人(他们也很棒)因为你的最后一句话,这是最有帮助和最重要的要记住..谢谢
  • 谢谢,这是我的直觉。如果您针对您的应用的具体情况提出其他问题,我可能会提供更多帮助。
  • @programmer 确实,@Aliostad 提出了一个非常有效的观点。作为参考,我有 95% 的时间有(感知到的)内存泄漏,结果证明是我没有取消订阅的事件,导致对象保留在图表。
  • 如果 microsoft 将 OutOfMemoryException 划分为一组更具体的内存相关异常 - 那会很棒..
【解决方案3】:

.NET 有一个兼容的垃圾收集器,因此您可能不会在普通的 .NET 堆上遇到碎片问题。但是,如果您使用大量非托管内存(例如通过 GDI+、COM 等),则可能会出现内存碎片。此外,大型对象堆没有被压缩,因此也会变得碎片化。 IIRC 如果对象大于 80kb,则将其放入 LOH。因此,如果您有许多包含超过 20k 个对象的集合,您可能会遇到碎片问题。

但与其猜测问题可能出在哪里,不如进一步缩小问题范围:您什么时候收到 OutOfMemoryExceptions?当时应用程序使用了多少内存?使用 WinDbg 或内存分析器之类的工具,您应该能够找出 LOH 上有多少内存。

也就是说,如果你知道的话,提前设置 List 和其他数据结构的容量总是一个好主意。否则,每次添加项目并达到容量限制时,列表的容量都会增加一倍,这意味着大量不必要的分配和复制操作。

【讨论】:

  • LOH 的一个(但不是唯一的)标准是该项目是 GTE 85kb - 而不是 80kb。 CLR 有时还会在 LOH 上放置其他较小的对象。无论大小如何,许多 byte[] 通常都会在这里结束。
【解决方案4】:

为了解决这个问题,您必须了解基础知识并查明代码中的问题。

如果您有一个合理的估计,设置初始容量总是一个好主意。如果您只有大概的猜测,请分配更多。

碎片只能发生在 LOH(超过 80 kB 的对象)上。为了防止它,尝试分配相同大小的块。矛盾的是,解决方案有时可能会分配 更多 内存而不是您实际需要的内存。

【讨论】:

  • LOH 的一个(但不是唯一的)标准是该项目是 GTE 85kb - 而不是 80kb。 CLR 有时还会在 LOH 上放置其他较小的对象。无论大小如何,许多 byte[] 通常都会在这里结束。
  • 也许==也许-不是
  • 你是说'也许'阵列被放置在那里并且'也许不是'?这是未记录的行为,因此您不能(或不应该)依赖它,因为它不受您的控制。可能我没看懂你的评论
【解决方案5】:

答案是,是的,预先定义集合的大小将提高性能和内存优化并减少碎片。在这里查看我的答案以了解原因 - If I set the initial size of a .NET collection and then add some items OVER this initial size, how does the collection determine the next resize?

但是,如果不对应用程序上的内存转储或内存分析进行分析,就无法准确说出 OOM 的原因是什么。因此,无法推测这种优化是否会解决问题。

【讨论】:

    猜你喜欢
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 2015-09-06
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    相关资源
    最近更新 更多