【问题标题】:Processing big strings, Is this Large Object Heap Fragmentation?处理大字符串,这是大对象堆碎片吗?
【发布时间】:2011-11-14 14:43:44
【问题描述】:

我有一个 .NET 3.5 应用程序

  • 一个函数运行了一百万次
  • 它在 1MB+ 字符串(不同大小的字符串)中进行搜索、替换和正则表达式操作

当我分析应用程序时,我可以确认这些字符串存储在 LOH 中,但它们稍后会被 GC 回收,因此在给定时间,其中最多只有 10 个在 LOH 中(10 个线程正在运行)。

我的理解是,这些大字符串位于 LOH 中,然后被 GC 回收,但不知何故由于它们的分配位置(并且在 LOH 中因此没有被压缩)这会导致碎片。尽管操作中没有内存泄漏,但仍会发生这种情况。

它在大约 100K 次内不会导致问题,但是当它达到 1M+ 时,它会出现内存不足异常。

我正在使用 ANTS Memory Profiler,这是我在早期执行中得到的结果:

.NET Using 70MB of 210MB total private bytes allocated in to the application
Number of Fragments: 59
Number of Large Fragments : 48 (99.6% of free memory)
Largest Fragment: 9MB
Free Space: 52% of total memory  (37MB)
Unmanaged Memory: 66% of total private memory (160MB)
  1. 根据现有数据,您认为我的诊断正确吗?
  2. 如果是这样,我该如何解决这个 LOH 碎片问题?我必须处理这些字符串,它们是大字符串。我应该找到一种方法将它们分开并像这样处理吗?在这种情况下,在拆分字符串中运行正则表达式等将非常具有挑战性。

【问题讨论】:

  • 另一种可能的解决方案:创建一个单独的进程来处理字符串,并为每个字符串使用一个新进程(或者每 100K,如果这对你的字符串来说很好,等等)。每个过程都从一张白纸开始。这是 IIS 回收应用程序池 - 碎片的原因之一。
  • @vcsjones,我在实际这样做之前就想到了这一点并且过度杀伤它:) 我想确定这是原因。我是 GC 细节的新手,所以我不希望几个小时后发现我实际上并没有解决任何问题!问题是重现实际问题非常困难,如果幸运的话,可能需要一两天。所以我大部分时间都接受分析者的话。
  • 您可以尝试以 64 位模式运行程序。这将解决问题,因为虚拟空间要大得多。
  • 我知道你问的是 3.5,但请看这里:connect.microsoft.com/VisualStudio/feedback/details/521147/… 首席程序员说他们在 4.0 中部分解决了问题。
  • 你知道它在 x64 上运行但为 x86 编译会发生什么吗?

标签: c# .net memory-leaks out-of-memory large-object-heap


【解决方案1】:
  1. 是的。听起来是对的。 LOH 变得碎片化,导致运行时无法为大字符串分配足够的连续空间。

  2. 你有几个选择,我想你应该选择最简单有效的一个。这完全取决于它的写作方式。

    1. 将您的字符串分成足够小的块,使它们不在 LOH 中。 (小于 85K - 注意:将对象放在 LOH isn't that cut-and-dry 上的逻辑。)这将允许 GC 能够回收空间。这决不能保证修复碎片 - 否则它肯定仍然会发生。如果你把琴弦做得更小,但仍然在 LOH 上 - 你会推迟这个问题。这取决于您需要处理多少超过 100 万个字符串。另一个缺点是 - 您仍然必须将字符串加载到内存中才能将其拆分,因此无论如何它最终都会出现在 LOH 上。您的应用程序甚至会在加载它们之前收缩字符串。有点像第 22 条军规。 编辑:cmets 中的 Gabe 指出,如果您可以先将字符串加载到 StringBuilder 中,那么在幕后,它会努力将事情排除在 LOH 之外(直到您致电 @987654323 @就可以了)。

    2. 将字符串的处理分成一个单独的进程。使用进程而不是线程。使用每个进程处理 10K 个字符串,然后终止该进程并启动另一个。这样,每个过程都从一张白纸开始。这样做的好处是它不会改变您的字符串处理逻辑(以防您不能使您的字符串更小以便处理),并避免#1 中的catch-22。缺点是这可能需要对您的应用程序进行更大的更改,并协调主进程和从属处理进程之间的工作。诀窍是主人只能告诉它大字符串在哪里,它不能直接给它,否则你回到catch-22。

【讨论】:

  • 一般来说,您可以将字符串加载到StringBuilder 中(至少在 .NET 4.0 中)非常小心地避免 LOH。这将很容易打破字符串。不幸的是,您不能在 StringBuilder 上运行正则表达式,并且可能也无法在断开的字符串上运行他需要的正则表达式。
猜你喜欢
  • 1970-01-01
  • 2010-10-15
  • 2011-12-22
  • 2012-08-01
  • 2011-07-12
  • 2017-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多