c# - 处理大字符串,这是大对象堆碎片吗?

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

我有一个 .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 碎片问题?我必须处理这些字符串,它们是大字符串。我应该找到一种方法将它们分开并像那样处理吗?在那种情况下,在拆分字符串中运行正则表达式等将非常具有挑战性。

最佳答案

  1. 是的。听起来不错。 LOH 变得碎片化,导致运行时无法为大字符串分配足够的连续空间。

  2. 您有几种选择,我想您应该选择最简单有效的方法。这完全取决于它的书写方式。

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

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

关于c# - 处理大字符串,这是大对象堆碎片吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7375964/

相关文章:

.net - 当 .NET 崩溃报告中的 P9 "bucket"包含乱码而不是导致崩溃的异常名称时,这意味着什么?

.NET 在 Windows 8 中访问 DB2

c# - Object() 的新实例

java - 如果GC后JConsole显示的 "Heap"内存使用量通常返回0,是否没有内存泄漏?

c# - SqlParameter.Add 和 AddWithValue 之间的区别

c# - 如何在不锁定文件的情况下将xml反序列化为对象?

c# - true== 有什么特别之处吗?

android - 消息处理程序和 WeakReference 问题

c++ - CComPtr 内存泄漏

c# - 手动发布到 Asp.Net MVC Controller